1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/misc/lvm-string.c

364 lines
7.9 KiB
C

/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lib.h"
#include "lvm-string.h"
#include <ctype.h>
int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...)
{
int n;
va_list ap;
va_start(ap, fmt);
n = vsnprintf(*buffer, *size, fmt, ap);
va_end(ap);
/*
* Revert to old glibc behaviour (version <= 2.0.6) where snprintf
* returned -1 if buffer was too small. From glibc 2.1 it returns number
* of chars that would have been written had there been room.
*/
if (n < 0 || ((unsigned) n + 1 > *size))
n = -1;
if (n < 0 || ((size_t)n == *size))
return 0;
*buffer += n;
*size -= n;
return 1;
}
/*
* Count occurences of 'c' in 'str' until we reach a null char.
*
* Returns:
* len - incremented for each char we encounter.
* count - number of occurrences of 'c' and 'c2'.
*/
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 == c1 || *ptr == c2)
(*count)++;
}
/*
* Count occurences of 'c' in 'str' of length 'size'.
*
* Returns:
* Number of occurrences of 'c'
*/
unsigned count_chars(const char *str, size_t len, const int c)
{
size_t i;
unsigned count = 0;
for (i = 0; i < len; i++)
if (str[i] == c)
count++;
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++;
}
}
static void _unquote_one_character(char *src, const char orig_char,
const char quote_char)
{
char *out;
char s, n;
/* Optimise for the common case where no changes are needed. */
while ((s = *src++)) {
if (s == quote_char &&
((n = *src) == orig_char || n == quote_char)) {
out = src++;
*(out - 1) = n;
while ((s = *src++)) {
if (s == quote_char &&
((n = *src) == orig_char || n == quote_char)) {
s = n;
src++;
}
*out = s;
out++;
}
*out = '\0';
return;
}
}
}
/*
* Unquote each character given in orig_char array and unquote quote_char
* as well. Also save the first occurrence of each character from orig_char
* that was found unquoted in arr_substr_first_unquoted array. This way we can
* process several characters in one go.
*/
static void _unquote_characters(char *src, const char *orig_chars,
const int num_orig_chars,
const char quote_char,
char *arr_substr_first_unquoted[])
{
char *out = src;
char c, s, n;
unsigned i;
while ((s = *src++)) {
for (i = 0; i < num_orig_chars; i++) {
c = orig_chars[i];
if (s == quote_char &&
((n = *src) == c || n == quote_char)) {
s = n;
src++;
break;
}
if (arr_substr_first_unquoted && (s == c) &&
!arr_substr_first_unquoted[i])
arr_substr_first_unquoted[i] = out;
};
*out++ = s;
}
*out = '\0';
}
/*
* Copies a string, quoting hyphens with hyphens.
*/
static void _quote_hyphens(char **out, const char *src)
{
_quote_characters(out, src, '-', '-', 0);
}
/*
* <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
*/
char *build_dm_name(struct dm_pool *mem, const char *vgname,
const char *lvname, const char *layer)
{
size_t len = 1;
int hyphens = 1;
char *r, *out;
_count_chars(vgname, &len, &hyphens, '-', 0);
_count_chars(lvname, &len, &hyphens, '-', 0);
if (layer && *layer) {
_count_chars(layer, &len, &hyphens, '-', 0);
hyphens++;
}
len += hyphens;
if (!(r = dm_pool_alloc(mem, len))) {
log_error("build_dm_name: Allocation failed for %" PRIsize_t
" for %s %s %s.", len, vgname, lvname, layer);
return NULL;
}
out = r;
_quote_hyphens(&out, vgname);
*out++ = '-';
_quote_hyphens(&out, lvname);
if (layer && *layer) {
/* No hyphen if the layer begins with _ e.g. _mlog */
if (*layer != '_')
*out++ = '-';
_quote_hyphens(&out, layer);
}
*out = '\0';
return r;
}
char *build_dm_uuid(struct dm_pool *mem, const char *lvid, const char *layer)
{
char *dmuuid;
size_t len;
if (!layer)
layer = "";
len = sizeof(UUID_PREFIX) + strlen(lvid) + strlen(layer) + 1;
if (!(dmuuid = dm_pool_alloc(mem, len))) {
log_error("build_dm_name: Allocation failed for %" PRIsize_t
" %s %s.", len, lvid, layer);
return NULL;
}
sprintf(dmuuid, UUID_PREFIX "%s%s%s", lvid, (*layer) ? "-" : "", layer);
return dmuuid;
}
/*
* 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_one_character(src, '\"', '\\');
}
/*
* Unescape colons and "at" signs in situ and save the substrings
* starting at the position of the first unescaped colon and the
* first unescaped "at" sign. This is normally used to unescape
* device names used as PVs.
*/
void unescape_colons_and_at_signs(char *src,
char **substr_first_unquoted_colon,
char **substr_first_unquoted_at_sign)
{
const char *orig_chars = ":@";
char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
_unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
if (substr_first_unquoted_colon)
*substr_first_unquoted_colon = arr_substr_first_unquoted[0];
if (substr_first_unquoted_at_sign)
*substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
}
/*
* Device layer names are all of the form <vg>-<lv>-<layer>, any
* other hyphens that appear in these names are quoted with yet
* another hyphen. The top layer of any device has no layer
* name. eg, vg0-lvol0.
*/
int validate_name(const char *n)
{
register char c;
register int len = 0;
if (!n || !*n)
return 0;
/* Hyphen used as VG-LV separator - ambiguity if LV starts with it */
if (*n == '-')
return 0;
if (!strcmp(n, ".") || !strcmp(n, ".."))
return 0;
while ((len++, c = *n++))
if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+')
return 0;
if (len > NAME_LEN)
return 0;
return 1;
}
int apply_lvname_restrictions(const char *name)
{
if (!strncmp(name, "snapshot", 8)) {
log_error("Names starting \"snapshot\" are reserved. "
"Please choose a different LV name.");
return 0;
}
if (!strncmp(name, "pvmove", 6)) {
log_error("Names starting \"pvmove\" are reserved. "
"Please choose a different LV name.");
return 0;
}
if (strstr(name, "_mlog")) {
log_error("Names including \"_mlog\" are reserved. "
"Please choose a different LV name.");
return 0;
}
if (strstr(name, "_mimage")) {
log_error("Names including \"_mimage\" are reserved. "
"Please choose a different LV name.");
return 0;
}
if (strstr(name, "_vorigin")) {
log_error("Names including \"_vorigin\" are reserved. "
"Please choose a different LV name.");
return 0;
}
return 1;
}
int is_reserved_lvname(const char *name)
{
int rc, old_suppress;
old_suppress = log_suppress(2);
rc = !apply_lvname_restrictions(name);
log_suppress(old_suppress);
return rc;
}