blob: b8d229b58b287d3b9283901ec32a935bd780f9b8 [file] [log] [blame]
/*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib.h"
#include "filter.h"
struct rfilter {
struct dm_pool *mem;
dm_bitset_t accept;
struct dm_regex *engine;
};
static int _extract_pattern(struct dm_pool *mem, const char *pat,
char **regex, dm_bitset_t accept, int ix)
{
char sep, *r, *ptr;
/*
* is this an accept or reject pattern
*/
switch (*pat) {
case 'a':
dm_bit_set(accept, ix);
break;
case 'r':
dm_bit_clear(accept, ix);
break;
default:
log_info("pattern must begin with 'a' or 'r'");
return 0;
}
pat++;
/*
* get the separator
*/
switch (*pat) {
case '(':
sep = ')';
break;
case '[':
sep = ']';
break;
case '{':
sep = '}';
break;
default:
sep = *pat;
}
pat++;
/*
* copy the regex
*/
if (!(r = dm_pool_strdup(mem, pat)))
return_0;
/*
* trim the trailing character, having checked it's sep.
*/
ptr = r + strlen(r) - 1;
if (*ptr != sep) {
log_info("invalid separator at end of regex");
return 0;
}
*ptr = '\0';
regex[ix] = r;
return 1;
}
static int _build_matcher(struct rfilter *rf, const struct dm_config_value *val)
{
struct dm_pool *scratch;
const struct dm_config_value *v;
char **regex;
unsigned count = 0;
int i, r = 0;
if (!(scratch = dm_pool_create("filter dm_regex", 1024)))
return_0;
/*
* count how many patterns we have.
*/
for (v = val; v; v = v->next) {
if (v->type != DM_CFG_STRING) {
log_error("Filter patterns must be enclosed in quotes.");
goto out;
}
count++;
}
/* Allocate space for them */
if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) {
log_error("Failed to allocate regex.");
goto out;
}
/* Create the accept/reject bitset */
if (!(rf->accept = dm_bitset_create(rf->mem, count))) {
log_error("Failed to create bitset.");
goto out;
}
/*
* fill the array back to front because we
* want the opposite precedence to what
* the matcher gives.
*/
for (v = val, i = count - 1; v; v = v->next, i--)
if (!_extract_pattern(scratch, v->v.str, regex, rf->accept, i)) {
log_error("Invalid filter pattern \"%s\".", v->v.str);
goto out;
}
/*
* build the matcher.
*/
if (!(rf->engine = dm_regex_create(rf->mem, (const char * const*) regex,
count)))
goto_out;
r = 1;
out:
dm_pool_destroy(scratch);
return r;
}
static int _accept_p(struct dev_filter *f, struct device *dev)
{
int m, first = 1, rejected = 0;
struct rfilter *rf = (struct rfilter *) f->private;
struct dm_str_list *sl;
dm_list_iterate_items(sl, &dev->aliases) {
m = dm_regex_match(rf->engine, sl->str);
if (m >= 0) {
if (dm_bit(rf->accept, m)) {
if (!first)
dev_set_preferred_name(sl, dev);
return 1;
}
rejected = 1;
}
first = 0;
}
if (rejected)
log_debug_devs("%s: Skipping (regex)", dev_name(dev));
/*
* pass everything that doesn't match
* anything.
*/
return !rejected;
}
static void _regex_destroy(struct dev_filter *f)
{
struct rfilter *rf = (struct rfilter *) f->private;
if (f->use_count)
log_error(INTERNAL_ERROR "Destroying regex filter while in use %u times.", f->use_count);
dm_pool_destroy(rf->mem);
}
struct dev_filter *regex_filter_create(const struct dm_config_value *patterns)
{
struct dm_pool *mem = dm_pool_create("filter regex", 10 * 1024);
struct rfilter *rf;
struct dev_filter *f;
if (!mem)
return_NULL;
if (!(rf = dm_pool_alloc(mem, sizeof(*rf))))
goto_bad;
rf->mem = mem;
if (!_build_matcher(rf, patterns))
goto_bad;
if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
goto_bad;
f->passes_filter = _accept_p;
f->destroy = _regex_destroy;
f->use_count = 0;
f->private = rf;
log_debug_devs("Regex filter initialised.");
return f;
bad:
dm_pool_destroy(mem);
return NULL;
}