mirror of
git://sourceware.org/git/lvm2.git
synced 2025-11-25 12:23:53 +03:00
devices_file, e.g. /etc/lvm/lvm_devices.dat, is a list of devices that lvm can use. Option --devicesfile can sepecify a different file with a separate set of devices for lvm to use. This option allows different applications to use lvm on different sets of devices. In most cases (with limited exceptions), lvm will not read or use a device not listed in devices_file. When the devices_file is used, the filter-regex is not used and the filter settings in lvm.conf are ignored. lvm uses the devices_file to control access to devices using the new filter-deviceid. Setting --devicesfile to "" on the command line, or devicesfile to "" in lvm.conf will disable the use of the devices_file and filter-deviceid. This allows lvm to see and use any device on the system. In this case lvm will fall back to using filter-regex and the filter config settings in lvm.conf. device_id, e.g. wwid or serial number from sysfs, is a unique ID that identifies a device without reading it, and which will change if the device is copied to another. The device_id is used in the devices_file and is included in VG metadata sections. Each device_id has a device_id_type which indicates where the device_id comes from, e.g. "sys_wwid" means the device_id comes from the sysfs wwid file. Others are sys_serial, mpath_uuid, loop_file, devname. (devname is the device path which is a fallback when no other proper device_id_type is available.) filter-deviceid permits lvm to use only devices on the system that have a device_id matching a devices_file entry. Using the device_id, lvm can determine the set of devices to use without reading any devices, so devices_file will constrain lvm in two ways: 1. it limits the devices that lvm will read. 2. it limits the devices that lvm will use. In some uncommon cases, e.g. when devices have no unique ID and device_id has to fall back to using the devname, lvm may need to read all devices on the system to determine which ones correspond to the devices_file entries. In this case, the devices_file does not limit the devices that lvm reads, but it does limit the devices that lvm uses. pvcreate/vgcreate/vgextend are not constrained by the devices_file, and will look outside it to find the new PV. They assign the new PV a device_id and add it to the devices_file. It would be possible to explicitly add new PVs to devices_file before using them in pvcreate/etc, in which case these commands would not need to access devices outside devices_file. (A config setting may be added to control the ability of these commands to search outside devices_list.) vgimportdevices looks at all devices on the system to find an existing VG and add its devices to the devices_file (it is not constrained by an existing devices_file.) It will also add device_ids to the VG metadata if the VG does not yet include device_ids. Use vgimportdevices -a to import devices for all accessible VGs. Since vgimportdevices does not limit itself to devices in an existing devices_file, the lvm.conf filter applies. Adding --foreign will import devices for foreign VGs, but device_ids are not added to foreign VGs. TODO: pvchange --deviceidupdate PV vgchange --deviceidupdate VG pvs -o deviceidtype,deviceid duplicate PV resolution using device_id vgimportclone config setting device_id_types to control idtypes used lvmdevices command to manage devices_file search for pvid when no wwid is available and PV is missing md device_id_type config setting pvcreate_extends_devices_file=0|1 shortsystemid crc of systemid and written in pv header use shortsystemid for new filter and orphan PV ownership TODO: new lvmdevices command: --add <devname> Adds devices_file entry, reads device header. --del <devname> Removes devices_file entry. --addpvid <pvid> Reads pv header of all devices to find <pvid>, if found adds devices_file entry. --delpvid <pvid> Removes devices_file entry. --adddeviceid <device_id> --deviceidtype <idtype> Reads <idtype> of all devices (e.g. from sysfs) to find a match, if found, reads pv header and adds devices_file entry. --deldeviceid <device_id> Removes devices_file entry.
231 lines
4.5 KiB
C
231 lines
4.5 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "lib/misc/lib.h"
|
|
#include "lib/filters/filter.h"
|
|
#include "lib/commands/toolcontext.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_error("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_error("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 cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
|
|
{
|
|
int m, first = 1, rejected = 0;
|
|
struct rfilter *rf = (struct rfilter *) f->private;
|
|
struct dm_str_list *sl;
|
|
|
|
if (cmd->enable_device_ids) {
|
|
/* TODO: print a notice if the filter is set to something and we ignore it here. */
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
f->name = "regex";
|
|
|
|
log_debug_devs("Regex filter initialised.");
|
|
|
|
return f;
|
|
|
|
bad:
|
|
dm_pool_destroy(mem);
|
|
return NULL;
|
|
}
|