| /* |
| * 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; |
| } |