Merge pull request #908 from richardmaw-codethink/nspawn-path-escapes-v3
Allow arbitrary file paths to be passed to nspawn (v3)
This commit is contained in:
@@ -550,7 +550,7 @@ char **replace_env_argv(char **argv, char **env) {
|
||||
if (e) {
|
||||
int r;
|
||||
|
||||
r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
|
||||
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
|
||||
if (r < 0) {
|
||||
ret[k] = NULL;
|
||||
strv_free(ret);
|
||||
|
||||
+24
-3
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
|
||||
return l;
|
||||
}
|
||||
|
||||
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
|
||||
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
|
||||
size_t n = 0, allocated = 0;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
@@ -289,11 +289,12 @@ int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = unquote_first_word(&s, &word, flags);
|
||||
r = extract_first_word(&s, &word, separators, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
if (r == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(l, allocated, n + 2))
|
||||
return -ENOMEM;
|
||||
@@ -693,6 +694,26 @@ char **strv_reverse(char **l) {
|
||||
return l;
|
||||
}
|
||||
|
||||
char **strv_shell_escape(char **l, const char *bad) {
|
||||
char **s;
|
||||
|
||||
/* Escapes every character in every string in l that is in bad,
|
||||
* edits in-place, does not roll-back on error. */
|
||||
|
||||
STRV_FOREACH(s, l) {
|
||||
char *v;
|
||||
|
||||
v = shell_escape(*s, bad);
|
||||
if (!v)
|
||||
return NULL;
|
||||
|
||||
free(*s);
|
||||
*s = v;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
|
||||
char* const* p;
|
||||
|
||||
|
||||
+2
-1
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
|
||||
char **strv_split(const char *s, const char *separator);
|
||||
char **strv_split_newlines(const char *s);
|
||||
|
||||
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
|
||||
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
|
||||
|
||||
char *strv_join(char **l, const char *separator);
|
||||
char *strv_join_quoted(char **l);
|
||||
@@ -145,6 +145,7 @@ void strv_print(char **l);
|
||||
}))
|
||||
|
||||
char **strv_reverse(char **l);
|
||||
char **strv_shell_escape(char **l, const char *bad);
|
||||
|
||||
bool strv_fnmatch(char* const* patterns, const char *s, int flags);
|
||||
|
||||
|
||||
+92
-46
@@ -4843,7 +4843,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -4883,7 +4883,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
const char *e;
|
||||
|
||||
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -5698,7 +5698,7 @@ int is_device_node(const char *path) {
|
||||
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
|
||||
}
|
||||
|
||||
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
size_t allocated = 0, sz = 0;
|
||||
int r;
|
||||
@@ -5711,13 +5711,19 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
SINGLE_QUOTE_ESCAPE,
|
||||
DOUBLE_QUOTE,
|
||||
DOUBLE_QUOTE_ESCAPE,
|
||||
SPACE,
|
||||
SEPARATOR,
|
||||
} state = START;
|
||||
|
||||
assert(p);
|
||||
assert(*p);
|
||||
assert(ret);
|
||||
|
||||
if (!separators)
|
||||
separators = WHITESPACE;
|
||||
|
||||
/* Bail early if called after last value or with no input */
|
||||
if (!*p)
|
||||
goto finish_force_terminate;
|
||||
|
||||
/* Parses the first word of a string, and returns it in
|
||||
* *ret. Removes all quotes in the process. When parsing fails
|
||||
* (because of an uneven number of quotes or similar), leaves
|
||||
@@ -5729,32 +5735,46 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
switch (state) {
|
||||
|
||||
case START:
|
||||
if (c == 0)
|
||||
goto finish;
|
||||
else if (strchr(WHITESPACE, c))
|
||||
if (c == 0) {
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
return -ENOMEM;
|
||||
goto finish_force_terminate;
|
||||
} else if (strchr(separators, c)) {
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
return -ENOMEM;
|
||||
(*p) ++;
|
||||
goto finish_force_next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
state = VALUE;
|
||||
/* fallthrough */
|
||||
|
||||
case VALUE:
|
||||
if (c == 0)
|
||||
goto finish;
|
||||
else if (c == '\'') {
|
||||
goto finish_force_terminate;
|
||||
else if (c == '\'' && (flags & EXTRACT_QUOTES)) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
return -ENOMEM;
|
||||
|
||||
state = SINGLE_QUOTE;
|
||||
} else if (c == '\\')
|
||||
state = VALUE_ESCAPE;
|
||||
else if (c == '\"') {
|
||||
else if (c == '\"' && (flags & EXTRACT_QUOTES)) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
return -ENOMEM;
|
||||
|
||||
state = DOUBLE_QUOTE;
|
||||
} else if (strchr(WHITESPACE, c))
|
||||
state = SPACE;
|
||||
else {
|
||||
} else if (strchr(separators, c)) {
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
||||
(*p) ++;
|
||||
goto finish_force_next;
|
||||
}
|
||||
state = SEPARATOR;
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -5765,8 +5785,8 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
|
||||
case SINGLE_QUOTE:
|
||||
if (c == 0) {
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
if (flags & EXTRACT_RELAX)
|
||||
goto finish_force_terminate;
|
||||
return -EINVAL;
|
||||
} else if (c == '\'')
|
||||
state = VALUE;
|
||||
@@ -5804,29 +5824,29 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
return -ENOMEM;
|
||||
|
||||
if (c == 0) {
|
||||
if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
|
||||
(state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
|
||||
if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
|
||||
(state == VALUE_ESCAPE || flags & EXTRACT_RELAX)) {
|
||||
/* If we find an unquoted trailing backslash and we're in
|
||||
* UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
|
||||
* EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
|
||||
* output.
|
||||
*
|
||||
* Unbalanced quotes will only be allowed in UNQUOTE_RELAX
|
||||
* mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
|
||||
* Unbalanced quotes will only be allowed in EXTRACT_RELAX
|
||||
* mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
|
||||
*/
|
||||
s[sz++] = '\\';
|
||||
goto finish;
|
||||
goto finish_force_terminate;
|
||||
}
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
if (flags & EXTRACT_RELAX)
|
||||
goto finish_force_terminate;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flags & UNQUOTE_CUNESCAPE) {
|
||||
if (flags & EXTRACT_CUNESCAPE) {
|
||||
uint32_t u;
|
||||
|
||||
r = cunescape_one(*p, (size_t) -1, &c, &u);
|
||||
if (r < 0) {
|
||||
if (flags & UNQUOTE_CUNESCAPE_RELAX) {
|
||||
if (flags & EXTRACT_CUNESCAPE_RELAX) {
|
||||
s[sz++] = '\\';
|
||||
s[sz++] = c;
|
||||
goto end_escape;
|
||||
@@ -5849,24 +5869,29 @@ end_escape:
|
||||
VALUE;
|
||||
break;
|
||||
|
||||
case SPACE:
|
||||
case SEPARATOR:
|
||||
if (c == 0)
|
||||
goto finish_force_terminate;
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
|
||||
goto finish_force_next;
|
||||
if (!strchr(separators, c))
|
||||
goto finish;
|
||||
if (!strchr(WHITESPACE, c))
|
||||
goto finish;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
(*p) ++;
|
||||
}
|
||||
|
||||
finish_force_terminate:
|
||||
*p = NULL;
|
||||
finish:
|
||||
if (!s) {
|
||||
*p = NULL;
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
finish_force_next:
|
||||
s[sz] = 0;
|
||||
*ret = s;
|
||||
s = NULL;
|
||||
@@ -5874,26 +5899,27 @@ finish:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unquote_first_word_and_warn(
|
||||
int extract_first_word_and_warn(
|
||||
const char **p,
|
||||
char **ret,
|
||||
UnquoteFlags flags,
|
||||
const char *separators,
|
||||
ExtractFlags flags,
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *rvalue) {
|
||||
/* Try to unquote it, if it fails, warn about it and try again but this
|
||||
* time using UNQUOTE_CUNESCAPE_RELAX to keep the backslashes verbatim
|
||||
* time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim
|
||||
* in invalid escape sequences. */
|
||||
const char *save;
|
||||
int r;
|
||||
|
||||
save = *p;
|
||||
r = unquote_first_word(p, ret, flags);
|
||||
if (r < 0 && !(flags&UNQUOTE_CUNESCAPE_RELAX)) {
|
||||
/* Retry it with UNQUOTE_CUNESCAPE_RELAX. */
|
||||
r = extract_first_word(p, ret, separators, flags);
|
||||
if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
|
||||
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
|
||||
*p = save;
|
||||
r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX);
|
||||
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
|
||||
if (r < 0)
|
||||
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
||||
"Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
|
||||
@@ -5904,7 +5930,7 @@ int unquote_first_word_and_warn(
|
||||
return r;
|
||||
}
|
||||
|
||||
int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
|
||||
int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
|
||||
va_list ap;
|
||||
char **l;
|
||||
int n = 0, i, c, r;
|
||||
@@ -5930,7 +5956,7 @@ int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
|
||||
l = newa0(char*, n);
|
||||
for (c = 0; c < n; c++) {
|
||||
|
||||
r = unquote_first_word(p, &l[c], flags);
|
||||
r = extract_first_word(p, &l[c], separators, flags);
|
||||
if (r < 0) {
|
||||
int j;
|
||||
|
||||
@@ -6511,6 +6537,32 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
|
||||
assert(bad);
|
||||
|
||||
for (; *s; s++) {
|
||||
if (*s == '\\' || strchr(bad, *s))
|
||||
*(t++) = '\\';
|
||||
|
||||
*(t++) = *s;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char *shell_escape(const char *s, const char *bad) {
|
||||
char *r, *t;
|
||||
|
||||
r = new(char, strlen(s)*2+1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
t = strcpy_backslash_escaped(r, s, bad);
|
||||
*t = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char *shell_maybe_quote(const char *s) {
|
||||
const char *p;
|
||||
char *r, *t;
|
||||
@@ -6537,13 +6589,7 @@ char *shell_maybe_quote(const char *s) {
|
||||
*(t++) = '"';
|
||||
t = mempcpy(t, s, p - s);
|
||||
|
||||
for (; *p; p++) {
|
||||
|
||||
if (strchr(SHELL_NEED_ESCAPE, *p))
|
||||
*(t++) = '\\';
|
||||
|
||||
*(t++) = *p;
|
||||
}
|
||||
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
|
||||
|
||||
*(t++)= '"';
|
||||
*t = 0;
|
||||
|
||||
+11
-8
@@ -854,15 +854,17 @@ int is_symlink(const char *path);
|
||||
int is_dir(const char *path, bool follow);
|
||||
int is_device_node(const char *path);
|
||||
|
||||
typedef enum UnquoteFlags {
|
||||
UNQUOTE_RELAX = 1,
|
||||
UNQUOTE_CUNESCAPE = 2,
|
||||
UNQUOTE_CUNESCAPE_RELAX = 4,
|
||||
} UnquoteFlags;
|
||||
typedef enum ExtractFlags {
|
||||
EXTRACT_RELAX = 1,
|
||||
EXTRACT_CUNESCAPE = 2,
|
||||
EXTRACT_CUNESCAPE_RELAX = 4,
|
||||
EXTRACT_QUOTES = 8,
|
||||
EXTRACT_DONT_COALESCE_SEPARATORS = 16,
|
||||
} ExtractFlags;
|
||||
|
||||
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
|
||||
int unquote_first_word_and_warn(const char **p, char **ret, UnquoteFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
|
||||
int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
|
||||
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
|
||||
int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
|
||||
int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
|
||||
|
||||
static inline void free_and_replace(char **s, char *v) {
|
||||
free(*s);
|
||||
@@ -917,6 +919,7 @@ void cmsg_close_all(struct msghdr *mh);
|
||||
|
||||
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
|
||||
|
||||
char *shell_escape(const char *s, const char *bad);
|
||||
char *shell_maybe_quote(const char *s);
|
||||
|
||||
int parse_mode(const char *s, mode_t *ret);
|
||||
|
||||
Reference in New Issue
Block a user