mirror of
https://github.com/systemd/systemd.git
synced 2025-02-04 21:47:31 +03:00
util: rework word parsing and c unescaping code
When parsing words from input files, optionally automatically unescape the passed strings, controllable via a new flags parameter. Make use of this in tmpfiles, and port everything else over, too. This improves parsing quite a bit, since we no longer have to process the same string multiple times with different calls, where an earlier call might corrupt the input for a later call.
This commit is contained in:
parent
be3ce3014e
commit
4034a06ddb
@ -143,7 +143,7 @@ static int spawn_getter(const char *getter, const char *url) {
|
|||||||
_cleanup_strv_free_ char **words = NULL;
|
_cleanup_strv_free_ char **words = NULL;
|
||||||
|
|
||||||
assert(getter);
|
assert(getter);
|
||||||
r = strv_split_quoted(&words, getter, false);
|
r = strv_split_quoted(&words, getter, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to split getter option: %m");
|
return log_error_errno(r, "Failed to split getter option: %m");
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ static int x11_read_data(Context *c) {
|
|||||||
if (in_section && first_word(l, "Option")) {
|
if (in_section && first_word(l, "Option")) {
|
||||||
_cleanup_strv_free_ char **a = NULL;
|
_cleanup_strv_free_ char **a = NULL;
|
||||||
|
|
||||||
r = strv_split_quoted(&a, l, false);
|
r = strv_split_quoted(&a, l, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ static int x11_read_data(Context *c) {
|
|||||||
} else if (!in_section && first_word(l, "Section")) {
|
} else if (!in_section && first_word(l, "Section")) {
|
||||||
_cleanup_strv_free_ char **a = NULL;
|
_cleanup_strv_free_ char **a = NULL;
|
||||||
|
|
||||||
r = strv_split_quoted(&a, l, false);
|
r = strv_split_quoted(&a, l, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -539,7 +539,7 @@ static int read_next_mapping(const char* filename,
|
|||||||
if (l[0] == 0 || l[0] == '#')
|
if (l[0] == 0 || l[0] == '#')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
r = strv_split_quoted(&b, l, false);
|
r = strv_split_quoted(&b, l, 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ static int condition_test_kernel_command_line(Condition *c) {
|
|||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
r = unquote_first_word(&p, &word, true);
|
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
|
|||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
int strv_split_quoted(char ***t, const char *s, bool relax) {
|
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
|
||||||
size_t n = 0, allocated = 0;
|
size_t n = 0, allocated = 0;
|
||||||
_cleanup_strv_free_ char **l = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
int r;
|
int r;
|
||||||
@ -289,7 +289,7 @@ int strv_split_quoted(char ***t, const char *s, bool relax) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
|
|
||||||
r = unquote_first_word(&s, &word, relax);
|
r = unquote_first_word(&s, &word, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
|
|||||||
char **strv_split(const char *s, const char *separator);
|
char **strv_split(const char *s, const char *separator);
|
||||||
char **strv_split_newlines(const char *s);
|
char **strv_split_newlines(const char *s);
|
||||||
|
|
||||||
int strv_split_quoted(char ***t, const char *s, bool relax);
|
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
|
||||||
|
|
||||||
char *strv_join(char **l, const char *separator);
|
char *strv_join(char **l, const char *separator);
|
||||||
char *strv_join_quoted(char **l);
|
char *strv_join_quoted(char **l);
|
||||||
|
@ -1347,6 +1347,125 @@ char *cescape(const char *s) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cunescape_one(const char *p, size_t length, char *ret) {
|
||||||
|
int r = 1;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
assert(*p);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (length != (size_t) -1 && length < 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (p[0]) {
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
*ret = '\a';
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
*ret = '\b';
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
*ret = '\f';
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
*ret = '\n';
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
*ret = '\r';
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
*ret = '\t';
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
*ret = '\v';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
*ret = '\\';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
*ret = '"';
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
*ret = '\'';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
/* This is an extension of the XDG syntax files */
|
||||||
|
*ret = ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x': {
|
||||||
|
/* hexadecimal encoding */
|
||||||
|
int a, b;
|
||||||
|
|
||||||
|
if (length != (size_t) -1 && length < 3)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
a = unhexchar(p[1]);
|
||||||
|
if (a < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
b = unhexchar(p[2]);
|
||||||
|
if (b < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* don't allow NUL bytes */
|
||||||
|
if (a == 0 && b == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*ret = (char) ((a << 4) | b);
|
||||||
|
r = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7': {
|
||||||
|
/* octal encoding */
|
||||||
|
int a, b, c, m;
|
||||||
|
|
||||||
|
if (length != (size_t) -1 && length < 4)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
a = unoctchar(p[0]);
|
||||||
|
if (a < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
b = unoctchar(p[1]);
|
||||||
|
if (b < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
c = unoctchar(p[2]);
|
||||||
|
if (c < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* don't allow NUL bytes */
|
||||||
|
if (a == 0 && b == 0 && c == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Don't allow bytes above 255 */
|
||||||
|
m = (a << 6) | (b << 3) | c;
|
||||||
|
if (m > 255)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*ret = (char) m;
|
||||||
|
r = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
|
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
|
||||||
char *r, *t;
|
char *r, *t;
|
||||||
const char *f;
|
const char *f;
|
||||||
@ -1366,115 +1485,27 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
|||||||
memcpy(r, prefix, pl);
|
memcpy(r, prefix, pl);
|
||||||
|
|
||||||
for (f = s, t = r + pl; f < s + length; f++) {
|
for (f = s, t = r + pl; f < s + length; f++) {
|
||||||
size_t remaining = s + length - f;
|
size_t remaining;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
remaining = s + length - f;
|
||||||
assert(remaining > 0);
|
assert(remaining > 0);
|
||||||
|
|
||||||
if (*f != '\\') { /* a literal literal */
|
if (*f != '\\' || remaining == 1) {
|
||||||
|
/* a literal literal, or a trailing backslash, copy verbatim */
|
||||||
*(t++) = *f;
|
*(t++) = *f;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--remaining == 0) { /* copy trailing backslash verbatim */
|
k = cunescape_one(f + 1, remaining - 1, t);
|
||||||
*(t++) = *f;
|
if (k < 0) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
f++;
|
|
||||||
|
|
||||||
switch (*f) {
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
*(t++) = '\a';
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
*(t++) = '\b';
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
*(t++) = '\f';
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
*(t++) = '\n';
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
*(t++) = '\r';
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
*(t++) = '\t';
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
*(t++) = '\v';
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
*(t++) = '\\';
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
*(t++) = '"';
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
*(t++) = '\'';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
/* This is an extension of the XDG syntax files */
|
|
||||||
*(t++) = ' ';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'x': {
|
|
||||||
/* hexadecimal encoding */
|
|
||||||
int a = -1, b = -1;
|
|
||||||
|
|
||||||
if (remaining >= 2) {
|
|
||||||
a = unhexchar(f[1]);
|
|
||||||
b = unhexchar(f[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a < 0 || b < 0 || (a == 0 && b == 0)) {
|
|
||||||
/* Invalid escape code, let's take it literal then */
|
/* Invalid escape code, let's take it literal then */
|
||||||
*(t++) = '\\';
|
*(t++) = '\\';
|
||||||
*(t++) = 'x';
|
continue;
|
||||||
} else {
|
|
||||||
*(t++) = (char) ((a << 4) | b);
|
|
||||||
f += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
f += k;
|
||||||
}
|
t++;
|
||||||
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7': {
|
|
||||||
/* octal encoding */
|
|
||||||
int a = -1, b = -1, c = -1;
|
|
||||||
|
|
||||||
if (remaining >= 3) {
|
|
||||||
a = unoctchar(f[0]);
|
|
||||||
b = unoctchar(f[1]);
|
|
||||||
c = unoctchar(f[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
|
|
||||||
/* Invalid escape code, let's take it literal then */
|
|
||||||
*(t++) = '\\';
|
|
||||||
*(t++) = f[0];
|
|
||||||
} else {
|
|
||||||
*(t++) = (char) ((a << 6) | (b << 3) | c);
|
|
||||||
f += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Invalid escape code, let's take it literal then */
|
|
||||||
*(t++) = '\\';
|
|
||||||
*(t++) = *f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*t = 0;
|
*t = 0;
|
||||||
@ -3411,7 +3442,7 @@ char **replace_env_argv(char **argv, char **env) {
|
|||||||
if (e) {
|
if (e) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = strv_split_quoted(&m, e, true);
|
r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
ret[k] = NULL;
|
ret[k] = NULL;
|
||||||
strv_free(ret);
|
strv_free(ret);
|
||||||
@ -6382,7 +6413,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
|
|||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
char *value = NULL;
|
char *value = NULL;
|
||||||
|
|
||||||
r = unquote_first_word(&p, &word, true);
|
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -6422,7 +6453,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
|
|||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
const char *e;
|
const char *e;
|
||||||
|
|
||||||
r = unquote_first_word(&p, &word, true);
|
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -7275,9 +7306,10 @@ int is_dir(const char* path, bool follow) {
|
|||||||
return !!S_ISDIR(st.st_mode);
|
return !!S_ISDIR(st.st_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int unquote_first_word(const char **p, char **ret, bool relax) {
|
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||||
_cleanup_free_ char *s = NULL;
|
_cleanup_free_ char *s = NULL;
|
||||||
size_t allocated = 0, sz = 0;
|
size_t allocated = 0, sz = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
START,
|
START,
|
||||||
@ -7335,7 +7367,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
|
|
||||||
case VALUE_ESCAPE:
|
case VALUE_ESCAPE:
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
if (relax)
|
if (flags & UNQUOTE_RELAX)
|
||||||
goto finish;
|
goto finish;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -7343,6 +7375,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (flags & UNQUOTE_CUNESCAPE) {
|
||||||
|
r = cunescape_one(*p, (size_t) -1, &c);
|
||||||
|
if (r < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
(*p) += r - 1;
|
||||||
|
}
|
||||||
|
|
||||||
s[sz++] = c;
|
s[sz++] = c;
|
||||||
state = VALUE;
|
state = VALUE;
|
||||||
|
|
||||||
@ -7350,7 +7390,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
|
|
||||||
case SINGLE_QUOTE:
|
case SINGLE_QUOTE:
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
if (relax)
|
if (flags & UNQUOTE_RELAX)
|
||||||
goto finish;
|
goto finish;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (c == '\'')
|
} else if (c == '\'')
|
||||||
@ -7368,7 +7408,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
|
|
||||||
case SINGLE_QUOTE_ESCAPE:
|
case SINGLE_QUOTE_ESCAPE:
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
if (relax)
|
if (flags & UNQUOTE_RELAX)
|
||||||
goto finish;
|
goto finish;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -7376,6 +7416,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (flags & UNQUOTE_CUNESCAPE) {
|
||||||
|
r = cunescape_one(*p, (size_t) -1, &c);
|
||||||
|
if (r < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
(*p) += r - 1;
|
||||||
|
}
|
||||||
|
|
||||||
s[sz++] = c;
|
s[sz++] = c;
|
||||||
state = SINGLE_QUOTE;
|
state = SINGLE_QUOTE;
|
||||||
break;
|
break;
|
||||||
@ -7398,7 +7446,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
|
|
||||||
case DOUBLE_QUOTE_ESCAPE:
|
case DOUBLE_QUOTE_ESCAPE:
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
if (relax)
|
if (flags & UNQUOTE_RELAX)
|
||||||
goto finish;
|
goto finish;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -7406,6 +7454,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
|||||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (flags & UNQUOTE_CUNESCAPE) {
|
||||||
|
r = cunescape_one(*p, (size_t) -1, &c);
|
||||||
|
if (r < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
(*p) += r - 1;
|
||||||
|
}
|
||||||
|
|
||||||
s[sz++] = c;
|
s[sz++] = c;
|
||||||
state = DOUBLE_QUOTE;
|
state = DOUBLE_QUOTE;
|
||||||
break;
|
break;
|
||||||
@ -7435,7 +7491,7 @@ finish:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int unquote_many_words(const char **p, ...) {
|
int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char **l;
|
char **l;
|
||||||
int n = 0, i, c, r;
|
int n = 0, i, c, r;
|
||||||
@ -7446,7 +7502,7 @@ int unquote_many_words(const char **p, ...) {
|
|||||||
assert(p);
|
assert(p);
|
||||||
|
|
||||||
/* Count how many words are expected */
|
/* Count how many words are expected */
|
||||||
va_start(ap, p);
|
va_start(ap, flags);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!va_arg(ap, char **))
|
if (!va_arg(ap, char **))
|
||||||
break;
|
break;
|
||||||
@ -7461,7 +7517,7 @@ int unquote_many_words(const char **p, ...) {
|
|||||||
l = newa0(char*, n);
|
l = newa0(char*, n);
|
||||||
for (c = 0; c < n; c++) {
|
for (c = 0; c < n; c++) {
|
||||||
|
|
||||||
r = unquote_first_word(p, &l[c], false);
|
r = unquote_first_word(p, &l[c], flags);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
@ -7477,7 +7533,7 @@ int unquote_many_words(const char **p, ...) {
|
|||||||
|
|
||||||
/* If we managed to parse all words, return them in the passed
|
/* If we managed to parse all words, return them in the passed
|
||||||
* in parameters */
|
* in parameters */
|
||||||
va_start(ap, p);
|
va_start(ap, flags);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
char **v;
|
char **v;
|
||||||
|
|
||||||
|
@ -1017,8 +1017,13 @@ int take_password_lock(const char *root);
|
|||||||
int is_symlink(const char *path);
|
int is_symlink(const char *path);
|
||||||
int is_dir(const char *path, bool follow);
|
int is_dir(const char *path, bool follow);
|
||||||
|
|
||||||
int unquote_first_word(const char **p, char **ret, bool relax);
|
typedef enum UnquoteFlags{
|
||||||
int unquote_many_words(const char **p, ...) _sentinel_;
|
UNQUOTE_RELAX = 1,
|
||||||
|
UNQUOTE_CUNESCAPE = 2,
|
||||||
|
} UnquoteFlags;
|
||||||
|
|
||||||
|
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
|
||||||
|
int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
|
||||||
|
|
||||||
int free_and_strdup(char **p, const char *s);
|
int free_and_strdup(char **p, const char *s);
|
||||||
|
|
||||||
|
@ -1384,7 +1384,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
|||||||
|
|
||||||
/* Parse columns */
|
/* Parse columns */
|
||||||
p = buffer;
|
p = buffer;
|
||||||
r = unquote_many_words(&p, &action, &name, &id, &description, &home, NULL);
|
r = unquote_many_words(&p, 0, &action, &name, &id, &description, &home, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("[%s:%u] Syntax error.", fname, line);
|
log_error("[%s:%u] Syntax error.", fname, line);
|
||||||
return r;
|
return r;
|
||||||
|
@ -165,7 +165,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted
|
|||||||
assert_se(p);
|
assert_se(p);
|
||||||
assert_se(streq(p, quoted));
|
assert_se(streq(p, quoted));
|
||||||
|
|
||||||
r = strv_split_quoted(&s, quoted, false);
|
r = strv_split_quoted(&s, quoted, 0);
|
||||||
assert_se(r == 0);
|
assert_se(r == 0);
|
||||||
assert_se(s);
|
assert_se(s);
|
||||||
STRV_FOREACH(t, s) {
|
STRV_FOREACH(t, s) {
|
||||||
@ -182,7 +182,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
|
|||||||
char **t;
|
char **t;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = strv_split_quoted(&s, quoted, false);
|
r = strv_split_quoted(&s, quoted, 0);
|
||||||
assert_se(r == 0);
|
assert_se(r == 0);
|
||||||
assert_se(s);
|
assert_se(s);
|
||||||
j = strv_join(s, " | ");
|
j = strv_join(s, " | ");
|
||||||
@ -199,7 +199,7 @@ static void test_invalid_unquote(const char *quoted) {
|
|||||||
char **s = NULL;
|
char **s = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = strv_split_quoted(&s, quoted, false);
|
r = strv_split_quoted(&s, quoted, 0);
|
||||||
assert_se(s == NULL);
|
assert_se(s == NULL);
|
||||||
assert_se(r == -EINVAL);
|
assert_se(r == -EINVAL);
|
||||||
}
|
}
|
||||||
|
@ -1283,64 +1283,76 @@ static void test_unquote_first_word(void) {
|
|||||||
char *t;
|
char *t;
|
||||||
|
|
||||||
p = original = "foobar waldo";
|
p = original = "foobar waldo";
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "foobar"));
|
assert_se(streq(t, "foobar"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 7);
|
assert_se(p == original + 7);
|
||||||
|
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "waldo"));
|
assert_se(streq(t, "waldo"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 12);
|
assert_se(p == original + 12);
|
||||||
|
|
||||||
assert_se(unquote_first_word(&p, &t, false) == 0);
|
assert_se(unquote_first_word(&p, &t, 0) == 0);
|
||||||
assert_se(!t);
|
assert_se(!t);
|
||||||
assert_se(p == original + 12);
|
assert_se(p == original + 12);
|
||||||
|
|
||||||
p = original = "\"foobar\" \'waldo\'";
|
p = original = "\"foobar\" \'waldo\'";
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "foobar"));
|
assert_se(streq(t, "foobar"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 9);
|
assert_se(p == original + 9);
|
||||||
|
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "waldo"));
|
assert_se(streq(t, "waldo"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 16);
|
assert_se(p == original + 16);
|
||||||
|
|
||||||
assert_se(unquote_first_word(&p, &t, false) == 0);
|
assert_se(unquote_first_word(&p, &t, 0) == 0);
|
||||||
assert_se(!t);
|
assert_se(!t);
|
||||||
assert_se(p == original + 16);
|
assert_se(p == original + 16);
|
||||||
|
|
||||||
p = original = "\"";
|
p = original = "\"";
|
||||||
assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
|
assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
|
||||||
assert_se(p == original + 1);
|
assert_se(p == original + 1);
|
||||||
|
|
||||||
p = original = "\'";
|
p = original = "\'";
|
||||||
assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
|
assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
|
||||||
assert_se(p == original + 1);
|
assert_se(p == original + 1);
|
||||||
|
|
||||||
p = original = "\'fooo";
|
p = original = "\'fooo";
|
||||||
assert_se(unquote_first_word(&p, &t, false) == -EINVAL);
|
assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
|
||||||
assert_se(p == original + 5);
|
assert_se(p == original + 5);
|
||||||
|
|
||||||
p = original = "\'fooo";
|
p = original = "\'fooo";
|
||||||
assert_se(unquote_first_word(&p, &t, true) > 0);
|
assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
|
||||||
assert_se(streq(t, "fooo"));
|
assert_se(streq(t, "fooo"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 5);
|
assert_se(p == original + 5);
|
||||||
|
|
||||||
p = original = "yay\'foo\'bar";
|
p = original = "yay\'foo\'bar";
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "yayfoobar"));
|
assert_se(streq(t, "yayfoobar"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 11);
|
assert_se(p == original + 11);
|
||||||
|
|
||||||
p = original = " foobar ";
|
p = original = " foobar ";
|
||||||
assert_se(unquote_first_word(&p, &t, false) > 0);
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
assert_se(streq(t, "foobar"));
|
assert_se(streq(t, "foobar"));
|
||||||
free(t);
|
free(t);
|
||||||
assert_se(p == original + 12);
|
assert_se(p == original + 12);
|
||||||
|
|
||||||
|
p = original = " foo\\ba\\x6ar ";
|
||||||
|
assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
|
||||||
|
assert_se(streq(t, "foo\ba\x6ar"));
|
||||||
|
free(t);
|
||||||
|
assert_se(p == original + 13);
|
||||||
|
|
||||||
|
p = original = " foo\\ba\\x6ar ";
|
||||||
|
assert_se(unquote_first_word(&p, &t, 0) > 0);
|
||||||
|
assert_se(streq(t, "foobax6ar"));
|
||||||
|
free(t);
|
||||||
|
assert_se(p == original + 13);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_unquote_many_words(void) {
|
static void test_unquote_many_words(void) {
|
||||||
@ -1348,7 +1360,7 @@ static void test_unquote_many_words(void) {
|
|||||||
char *a, *b, *c;
|
char *a, *b, *c;
|
||||||
|
|
||||||
p = original = "foobar waldi piep";
|
p = original = "foobar waldi piep";
|
||||||
assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 3);
|
assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 3);
|
||||||
assert_se(p == original + 17);
|
assert_se(p == original + 17);
|
||||||
assert_se(streq_ptr(a, "foobar"));
|
assert_se(streq_ptr(a, "foobar"));
|
||||||
assert_se(streq_ptr(b, "waldi"));
|
assert_se(streq_ptr(b, "waldi"));
|
||||||
@ -1358,7 +1370,7 @@ static void test_unquote_many_words(void) {
|
|||||||
free(c);
|
free(c);
|
||||||
|
|
||||||
p = original = "'foobar' wa\"ld\"i ";
|
p = original = "'foobar' wa\"ld\"i ";
|
||||||
assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 2);
|
assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 2);
|
||||||
assert_se(p == original + 19);
|
assert_se(p == original + 19);
|
||||||
assert_se(streq_ptr(a, "foobar"));
|
assert_se(streq_ptr(a, "foobar"));
|
||||||
assert_se(streq_ptr(b, "waldi"));
|
assert_se(streq_ptr(b, "waldi"));
|
||||||
@ -1367,31 +1379,31 @@ static void test_unquote_many_words(void) {
|
|||||||
free(b);
|
free(b);
|
||||||
|
|
||||||
p = original = "";
|
p = original = "";
|
||||||
assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 0);
|
assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
|
||||||
assert_se(p == original);
|
assert_se(p == original);
|
||||||
assert_se(streq_ptr(a, NULL));
|
assert_se(streq_ptr(a, NULL));
|
||||||
assert_se(streq_ptr(b, NULL));
|
assert_se(streq_ptr(b, NULL));
|
||||||
assert_se(streq_ptr(c, NULL));
|
assert_se(streq_ptr(c, NULL));
|
||||||
|
|
||||||
p = original = " ";
|
p = original = " ";
|
||||||
assert_se(unquote_many_words(&p, &a, &b, &c, NULL) == 0);
|
assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
|
||||||
assert_se(p == original+2);
|
assert_se(p == original+2);
|
||||||
assert_se(streq_ptr(a, NULL));
|
assert_se(streq_ptr(a, NULL));
|
||||||
assert_se(streq_ptr(b, NULL));
|
assert_se(streq_ptr(b, NULL));
|
||||||
assert_se(streq_ptr(c, NULL));
|
assert_se(streq_ptr(c, NULL));
|
||||||
|
|
||||||
p = original = "foobar";
|
p = original = "foobar";
|
||||||
assert_se(unquote_many_words(&p, NULL) == 0);
|
assert_se(unquote_many_words(&p, 0, NULL) == 0);
|
||||||
assert_se(p == original);
|
assert_se(p == original);
|
||||||
|
|
||||||
p = original = "foobar waldi";
|
p = original = "foobar waldi";
|
||||||
assert_se(unquote_many_words(&p, &a, NULL) == 1);
|
assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
|
||||||
assert_se(p == original+7);
|
assert_se(p == original+7);
|
||||||
assert_se(streq_ptr(a, "foobar"));
|
assert_se(streq_ptr(a, "foobar"));
|
||||||
free(a);
|
free(a);
|
||||||
|
|
||||||
p = original = " foobar ";
|
p = original = " foobar ";
|
||||||
assert_se(unquote_many_words(&p, &a, NULL) == 1);
|
assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
|
||||||
assert_se(p == original+15);
|
assert_se(p == original+15);
|
||||||
assert_se(streq_ptr(a, "foobar"));
|
assert_se(streq_ptr(a, "foobar"));
|
||||||
free(a);
|
free(a);
|
||||||
|
@ -620,7 +620,6 @@ static int path_set_perms(Item *i, const char *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int get_xattrs_from_arg(Item *i) {
|
static int get_xattrs_from_arg(Item *i) {
|
||||||
char *xattr;
|
|
||||||
const char *p;
|
const char *p;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -629,35 +628,33 @@ static int get_xattrs_from_arg(Item *i) {
|
|||||||
|
|
||||||
p = i->argument;
|
p = i->argument;
|
||||||
|
|
||||||
while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
|
for (;;) {
|
||||||
_cleanup_free_ char *tmp = NULL, *name = NULL,
|
_cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
|
||||||
*value = NULL, *value2 = NULL, *_xattr = xattr;
|
|
||||||
|
r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
|
||||||
|
if (r < 0)
|
||||||
|
log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", p);
|
||||||
|
if (r <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
r = split_pair(xattr, "=", &name, &value);
|
r = split_pair(xattr, "=", &name, &value);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
|
log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strempty(name) || strempty(value)) {
|
if (isempty(name) || isempty(value)) {
|
||||||
log_warning("Malformed xattr found: \"%s\" - ignoring.", xattr);
|
log_warning("Malformed xattr found, ignoring: %s", xattr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp = unquote(value, "\"");
|
if (strv_push_pair(&i->xattrs, name, value) < 0)
|
||||||
if (!tmp)
|
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
value2 = cunescape(tmp);
|
name = value = NULL;
|
||||||
if (!value2)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (strv_push_pair(&i->xattrs, name, value2) < 0)
|
|
||||||
return log_oom();
|
|
||||||
name = value2 = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int path_set_xattrs(Item *i, const char *path) {
|
static int path_set_xattrs(Item *i, const char *path) {
|
||||||
@ -690,8 +687,7 @@ static int get_acls_from_arg(Item *item) {
|
|||||||
* afterwards, so the mask can be added now if necessary. */
|
* afterwards, so the mask can be added now if necessary. */
|
||||||
r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
|
r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring",
|
log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
|
||||||
item->argument);
|
|
||||||
#else
|
#else
|
||||||
log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
|
log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
|
||||||
#endif
|
#endif
|
||||||
@ -918,8 +914,7 @@ static int write_one_file(Item *i, const char *path) {
|
|||||||
if (i->argument) {
|
if (i->argument) {
|
||||||
_cleanup_free_ char *unescaped;
|
_cleanup_free_ char *unescaped;
|
||||||
|
|
||||||
log_debug("%s to \"%s\".",
|
log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
|
||||||
i->type == CREATE_FILE ? "Appending" : "Writing", path);
|
|
||||||
|
|
||||||
unescaped = cunescape(i->argument);
|
unescaped = cunescape(i->argument);
|
||||||
if (!unescaped)
|
if (!unescaped)
|
||||||
@ -1651,14 +1646,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
|||||||
assert(line >= 1);
|
assert(line >= 1);
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
|
|
||||||
r = unquote_many_words(&buffer,
|
r = unquote_many_words(
|
||||||
|
&buffer,
|
||||||
|
0,
|
||||||
&action,
|
&action,
|
||||||
&path,
|
&path,
|
||||||
&mode,
|
&mode,
|
||||||
&user,
|
&user,
|
||||||
&group,
|
&group,
|
||||||
&age,
|
&age,
|
||||||
&i.argument,
|
|
||||||
NULL);
|
NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
|
return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
|
||||||
@ -1667,6 +1663,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isempty(buffer)) {
|
||||||
|
i.argument = strdup(buffer);
|
||||||
|
if (!i.argument)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
if (isempty(action)) {
|
if (isempty(action)) {
|
||||||
log_error("[%s:%u] Command too short '%s'.", fname, line, action);
|
log_error("[%s:%u] Command too short '%s'.", fname, line, action);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user