diff --git a/WHATS_NEW b/WHATS_NEW index 52e99838e..167eb7c5e 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,8 @@ Version 2.02.34 - =================================== + Escape double quotes and backslashes in external metadata and config data. + Add functions for escaping double quotes in strings. + Rename count_chars_len to count_chars. Use return_0 in a couple more places. Correct a function name typo in _line_append error message. Include limits.h in clvmd so it compiles with newer headers. diff --git a/lib/config/config.c b/lib/config/config.c index 64542686f..f0582a5de 100644 --- a/lib/config/config.c +++ b/lib/config/config.c @@ -34,7 +34,8 @@ enum { TOK_INT, TOK_FLOAT, - TOK_STRING, + TOK_STRING, /* Single quotes */ + TOK_STRING_ESCAPED, /* Double quotes */ TOK_EQ, TOK_SECTION_B, TOK_SECTION_E, @@ -401,9 +402,16 @@ static int _line_end(struct output_line *outline) static int _write_value(struct output_line *outline, struct config_value *v) { + char *buf; + switch (v->type) { case CFG_STRING: - line_append("\"%s\"", v->v.str); + if (!(buf = alloca(escaped_len(v->v.str)))) { + log_error("temporary stack allocation for a config " + "string failed"); + return 0; + } + line_append("\"%s\"", escape_double_quotes(buf, v->v.str)); break; case CFG_FLOAT: @@ -644,6 +652,17 @@ static struct config_value *_type(struct parser *p) match(TOK_STRING); break; + case TOK_STRING_ESCAPED: + v->type = CFG_STRING; + + p->tb++, p->te--; /* strip "'s */ + if (!(v->v.str = _dup_tok(p))) + return_0; + unescape_double_quotes(v->v.str); + p->te++; + match(TOK_STRING_ESCAPED); + break; + default: log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value", p->tb - p->fb + 1, p->line); @@ -714,7 +733,7 @@ static void _get_token(struct parser *p, int tok_prev) break; case '"': - p->t = TOK_STRING; + p->t = TOK_STRING_ESCAPED; p->te++; while ((p->te != p->fe) && (*p->te) && (*p->te != '"')) { if ((*p->te == '\\') && (p->te + 1 != p->fe) && @@ -1232,7 +1251,7 @@ static unsigned _count_tokens(const char *str, unsigned len, int type) c = _token_type_to_char(type); - return count_chars_len(str, len, c); + return count_chars(str, len, c); } /* diff --git a/lib/filters/filter-persistent.c b/lib/filters/filter-persistent.c index bf92fc8ca..7d491e702 100644 --- a/lib/filters/filter-persistent.c +++ b/lib/filters/filter-persistent.c @@ -18,6 +18,7 @@ #include "dev-cache.h" #include "filter-persistent.h" #include "lvm-file.h" +#include "lvm-string.h" #include #include @@ -142,6 +143,7 @@ static void _write_array(struct pfilter *pf, FILE *fp, const char *path, { void *d; int first = 1; + char *buf, *str; struct dm_hash_node *n; for (n = dm_hash_get_first(pf->devices); n; @@ -158,7 +160,13 @@ static void _write_array(struct pfilter *pf, FILE *fp, const char *path, first = 0; } - fprintf(fp, "\t\t\"%s\"", dm_hash_get_key(pf->devices, n)); + str = dm_hash_get_key(pf->devices, n); + if (!(buf = alloca(escaped_len(str)))) { + log_error("persistent filter device path stack " + "allocation failed"); + return; + } + fprintf(fp, "\t\t\"%s\"", escape_double_quotes(buf, str)); } if (!first) diff --git a/lib/format_text/export.c b/lib/format_text/export.c index b88e18ac1..b9c047968 100644 --- a/lib/format_text/export.c +++ b/lib/format_text/export.c @@ -296,6 +296,7 @@ int out_text(struct formatter *f, const char *fmt, ...) static int _print_header(struct formatter *f, const char *desc) { + char *buf; time_t t; t = time(NULL); @@ -305,7 +306,12 @@ static int _print_header(struct formatter *f, outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE); outnl(f); - outf(f, "description = \"%s\"", desc); + if (!(buf = alloca(escaped_len(desc)))) { + log_error("temporary stack allocation for description" + "string failed"); + return 0; + } + outf(f, "description = \"%s\"", escape_double_quotes(buf, desc)); outnl(f); outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename, _utsname.sysname, _utsname.nodename, _utsname.release, @@ -370,6 +376,7 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) struct pv_list *pvl; struct physical_volume *pv; char buffer[4096]; + char *buf; const char *name; outf(f, "physical_volumes {"); @@ -389,7 +396,15 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) return_0; outf(f, "id = \"%s\"", buffer); - if (!out_hint(f, "device = \"%s\"", pv_dev_name(pv))) + + if (!(buf = alloca(escaped_len(pv_dev_name(pv))))) { + log_error("temporary stack allocation for device name" + "string failed"); + return 0; + } + + if (!out_hint(f, "device = \"%s\"", + escape_double_quotes(buf, pv_dev_name(pv)))) return_0; outnl(f); diff --git a/lib/misc/lvm-string.c b/lib/misc/lvm-string.c index 5f26ad40f..c75a9a7a8 100644 --- a/lib/misc/lvm-string.c +++ b/lib/misc/lvm-string.c @@ -39,16 +39,16 @@ int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...) * Count occurences of 'c' in 'str' until we reach a null char. * * Returns: - * len - incremented for each char we encounter, whether 'c' or not. - * count - number of occurrences of 'c' + * len - incremented for each char we encounter. + * count - number of occurrences of 'c' and 'c2'. */ -void count_chars(const char *str, size_t *len, int *count, - const int c) +static void _count_chars(const char *str, size_t *len, int *count, + const int c1, const int c2) { const char *ptr; for (ptr = str; *ptr; ptr++, (*len)++) - if (*ptr == c) + if (*ptr == c1 || *ptr == c2) (*count)++; } @@ -58,7 +58,7 @@ void count_chars(const char *str, size_t *len, int *count, * Returns: * Number of occurrences of 'c' */ -unsigned count_chars_len(const char *str, size_t len, const int c) +unsigned count_chars(const char *str, size_t len, const int c) { size_t i; unsigned count = 0; @@ -70,17 +70,62 @@ unsigned count_chars_len(const char *str, size_t len, const int c) return count; } +/* + * Length of string after escaping double quotes and backslashes. + */ +size_t escaped_len(const char *str) +{ + size_t len = 1; + int count = 0; + + _count_chars(str, &len, &count, '\"', '\\'); + + return count + len; +} + +/* + * Copies a string, quoting orig_char with quote_char. + * Optionally also quote quote_char. + */ +static void _quote_characters(char **out, const char *src, + const int orig_char, const int quote_char, + int quote_quote_char) +{ + while (*src) { + if (*src == orig_char || + (*src == quote_char && quote_quote_char)) + *(*out)++ = quote_char; + + *(*out)++ = *src++; + } +} + +/* + * Unquote orig_char in string. + * Also unquote quote_char. + */ +static void _unquote_characters(char *src, const int orig_char, + const int quote_char) +{ + char *out = src; + + while (*src) { + if (*src == quote_char && + (*(src + 1) == orig_char || *(src + 1) == quote_char)) + src++; + + *out++ = *src++; + } + + *out = '\0'; +} + /* * Copies a string, quoting hyphens with hyphens. */ static void _quote_hyphens(char **out, const char *src) { - while (*src) { - if (*src == '-') - *(*out)++ = '-'; - - *(*out)++ = *src++; - } + return _quote_characters(out, src, '-', '-', 0); } /* @@ -93,11 +138,11 @@ char *build_dm_name(struct dm_pool *mem, const char *vgname, int hyphens = 1; char *r, *out; - count_chars(vgname, &len, &hyphens, '-'); - count_chars(lvname, &len, &hyphens, '-'); + _count_chars(vgname, &len, &hyphens, '-', 0); + _count_chars(lvname, &len, &hyphens, '-', 0); if (layer && *layer) { - count_chars(layer, &len, &hyphens, '-'); + _count_chars(layer, &len, &hyphens, '-', 0); hyphens++; } @@ -125,6 +170,27 @@ char *build_dm_name(struct dm_pool *mem, const char *vgname, return r; } +/* + * Copies a string, quoting double quotes with backslashes. + */ +char *escape_double_quotes(char *out, const char *src) +{ + char *buf = out; + + _quote_characters(&buf, src, '\"', '\\', 1); + *buf = '\0'; + + return out; +} + +/* + * Undo quoting in situ. + */ +void unescape_double_quotes(char *src) +{ + _unquote_characters(src, '\"', '\\'); +} + /* * Device layer names are all of the form --, any * other hyphens that appear in these names are quoted with yet diff --git a/lib/misc/lvm-string.h b/lib/misc/lvm-string.h index 0fe38bfd2..7f50a97ce 100644 --- a/lib/misc/lvm-string.h +++ b/lib/misc/lvm-string.h @@ -31,8 +31,27 @@ char *build_dm_name(struct dm_pool *mem, const char *vg, int validate_name(const char *n); -void count_chars(const char *str, size_t *len, int *count, - const int c); -unsigned count_chars_len(const char *str, size_t len, const int c); +/* + * Returns number of occurrences of c in first len characters of str. + */ +unsigned count_chars(const char *str, size_t len, const int c); + +/* + * Returns what length of escaped string would be including terminating NUL. + */ +size_t escaped_len(const char *str); + +/* + * Copies a string from src to out. + * Double quotation marks and backslashes are quoted with a backslash. + * Caller must ensure *out has enough space - see escaped_len(). + * Returns *out. + */ +char *escape_double_quotes(char *out, const char *src); + +/* + * Removes quoting of double quotation marks and backslashes in situ. + */ +void unescape_double_quotes(char *src); #endif