From a8537e6f59c5ac1ffe08c01fd23d534030a06be9 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Fri, 13 Feb 2004 14:46:04 +0000 Subject: [PATCH] Sysfs block device filtering option for 2.6. --- doc/example.conf | 4 + include/.symlinks | 1 + lib/Makefile.in | 1 + lib/commands/toolcontext.c | 37 +++-- lib/config/defaults.h | 1 + lib/filters/filter-composite.c | 27 ++-- lib/filters/filter-composite.h | 2 +- lib/filters/filter-sysfs.c | 273 +++++++++++++++++++++++++++++++++ lib/filters/filter-sysfs.h | 15 ++ 9 files changed, 332 insertions(+), 29 deletions(-) create mode 100644 lib/filters/filter-sysfs.c create mode 100644 lib/filters/filter-sysfs.h diff --git a/doc/example.conf b/doc/example.conf index 1db94a858..9e832ab96 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -61,6 +61,10 @@ devices { # List of pairs of additional acceptable block device types found # in /proc/devices with maximum (non-zero) number of partitions. # types = [ "fd", 16 ] + + # If sysfs is mounted (2.6 kernels) restrict device scanning to + # the block devices it believes are valid. + sysfs_scan = 1 } # This section that allows you to configure the nature of the diff --git a/include/.symlinks b/include/.symlinks index 9a5335cb5..51faa2759 100644 --- a/include/.symlinks +++ b/include/.symlinks @@ -16,6 +16,7 @@ ../lib/filters/filter-composite.h ../lib/filters/filter-persistent.h ../lib/filters/filter-regex.h +../lib/filters/filter-sysfs.h ../lib/filters/filter.h ../lib/format1/format1.h ../lib/format_text/format-text.h diff --git a/lib/Makefile.in b/lib/Makefile.in index 355950bf1..cbe6c96ac 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -28,6 +28,7 @@ SOURCES=\ filters/filter-composite.c \ filters/filter-persistent.c \ filters/filter-regex.c \ + filters/filter-sysfs.c \ filters/filter.c \ format_text/archive.c \ format_text/export.c \ diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c index 496ac6160..ed81cf88e 100644 --- a/lib/commands/toolcontext.c +++ b/lib/commands/toolcontext.c @@ -16,6 +16,7 @@ #include "filter-composite.h" #include "filter-persistent.h" #include "filter-regex.h" +#include "filter-sysfs.h" #include "label.h" #include "lvm-file.h" #include "format-text.h" @@ -261,33 +262,43 @@ static int _init_dev_cache(struct cmd_context *cmd) return 1; } +#define MAX_FILTERS 3 + static struct dev_filter *_init_filter_components(struct cmd_context *cmd) { + unsigned nr_filt = 0; struct config_node *cn; - struct dev_filter *f1, *f2, *f3; + struct dev_filter *filters[MAX_FILTERS]; - cn = find_config_node(cmd->cf->root, "devices/types", '/'); + memset(filters, 0, sizeof(filters)); - if (!(f2 = lvm_type_filter_create(cmd->proc_dir, cn))) - return NULL; - - if (!(cn = find_config_node(cmd->cf->root, "devices/filter", '/'))) { - log_debug("devices/filter not found in config file: no regex " - "filter installed"); - return f2; + /* sysfs filter */ + if (find_config_bool(cmd->cf->root, "devices/sysfs_scan", '/', + DEFAULT_SYSFS_SCAN)) { + if ((filters[nr_filt] = sysfs_filter_create(cmd->proc_dir))) + nr_filt++; } - if (!(f1 = regex_filter_create(cn->v))) { + /* regex filter */ + if (!(cn = find_config_node(cmd->cf->root, "devices/filter", '/'))) + log_debug("devices/filter not found in config file: no regex " + "filter installed"); + + else if (!(filters[nr_filt++] = regex_filter_create(cn->v))) { log_error("Failed to create regex device filter"); return NULL; } - if (!(f3 = composite_filter_create(2, f1, f2))) { - log_error("Failed to create composite device filter"); + /* device type filter */ + cn = find_config_node(cmd->cf->root, "devices/types", '/'); + if (!(filters[nr_filt++] = lvm_type_filter_create(cmd->proc_dir, cn))) { + log_error("Failed to create lvm type filter"); return NULL; } - return f3; + /* only build a composite filter if we really need it */ + return (nr_filt == 1) ? + filters[0] : composite_filter_create(nr_filt, filters); } static int _init_filters(struct cmd_context *cmd) diff --git a/lib/config/defaults.h b/lib/config/defaults.h index bcebfa04f..b85b3d5d1 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -19,6 +19,7 @@ #define DEFAULT_SYS_DIR "/etc/lvm" #define DEFAULT_DEV_DIR "/dev" #define DEFAULT_PROC_DIR "/proc" +#define DEFAULT_SYSFS_SCAN 1 #define DEFAULT_LOCK_DIR "/var/lock/lvm" #define DEFAULT_LOCKING_LIB "lvm2_locking.so" diff --git a/lib/filters/filter-composite.c b/lib/filters/filter-composite.c index 78e41dc8c..a167abb0a 100644 --- a/lib/filters/filter-composite.c +++ b/lib/filters/filter-composite.c @@ -37,35 +37,32 @@ static void _destroy(struct dev_filter *f) dbg_free(f); } -struct dev_filter *composite_filter_create(int n, ...) +struct dev_filter *composite_filter_create(int n, struct dev_filter **filters) { - struct dev_filter **filters = dbg_malloc(sizeof(*filters) * (n + 1)); - struct dev_filter *cf; - va_list ap; - int i; + struct dev_filter **filters_copy, *cf; if (!filters) { stack; return NULL; } - if (!(cf = dbg_malloc(sizeof(*cf)))) { - stack; - dbg_free(filters); + if (!(filters_copy = dbg_malloc(sizeof(*filters) * (n + 1)))) { + log_error("composite filters allocation failed"); return NULL; } - va_start(ap, n); - for (i = 0; i < n; i++) { - struct dev_filter *f = va_arg(ap, struct dev_filter *); - filters[i] = f; + memcpy(filters_copy, filters, sizeof(*filters) * n); + filters_copy[n] = NULL; + + if (!(cf = dbg_malloc(sizeof(*cf)))) { + log_error("compsoite filters allocation failed"); + dbg_free(filters_copy); + return NULL; } - filters[i] = NULL; - va_end(ap); cf->passes_filter = _and_p; cf->destroy = _destroy; - cf->private = filters; + cf->private = filters_copy; return cf; } diff --git a/lib/filters/filter-composite.h b/lib/filters/filter-composite.h index 7462e4e22..b269381fc 100644 --- a/lib/filters/filter-composite.h +++ b/lib/filters/filter-composite.h @@ -9,6 +9,6 @@ #include "dev-cache.h" -struct dev_filter *composite_filter_create(int n, ...); +struct dev_filter *composite_filter_create(int n, struct dev_filter **filters); #endif diff --git a/lib/filters/filter-sysfs.c b/lib/filters/filter-sysfs.c new file mode 100644 index 000000000..c04bb9dd7 --- /dev/null +++ b/lib/filters/filter-sysfs.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2004 Red Hat Inc + * + * This file is released under the GPL. + */ + +#include "lib.h" +#include "filter-sysfs.h" +#include "lvm-string.h" +#include "pool.h" + +#include +#include +#include +#include + +static int _locate_sysfs_blocks(const char *proc, char *path, size_t len) +{ + char proc_mounts[PATH_MAX]; + int r = 0; + FILE *fp; + char *split[2], buffer[PATH_MAX + 16]; + + if (!*proc) { + log_verbose("No proc filesystem found: skipping sysfs filter"); + return 0; + } + + if (lvm_snprintf(proc_mounts, sizeof(proc_mounts), + "%s/mounts", proc) < 0) { + log_error("Failed to create /proc/mounts string"); + return 0; + } + + if (!(fp = fopen(proc_mounts, "r"))) { + log_sys_error("fopen %s", proc_mounts); + return 0; + } + + while (fgets(buffer, sizeof(buffer), fp)) { + if (split_words(buffer, 2, split) == 2 && + !strcmp(split[0], "sysfs")) { + if (lvm_snprintf(path, len, "%s/%s", split[1], + "block") >= 0) { + r = 1; + } + break; + } + } + + fclose(fp); + return r; +} + +/*---------------------------------------------------------------- + * We need to store a set of dev_t. + *--------------------------------------------------------------*/ +struct entry { + struct entry *next; + dev_t dev; +}; + +#define SET_BUCKETS 64 +struct dev_set { + struct pool *mem; + const char *sys_block; + int initialised; + struct entry *slots[SET_BUCKETS]; +}; + +static struct dev_set *_dev_set_create(struct pool *mem, const char *sys_block) +{ + struct dev_set *ds; + + if (!(ds = pool_zalloc(mem, sizeof(*ds)))) + return NULL; + + ds->mem = mem; + ds->sys_block = pool_strdup(mem, sys_block); + ds->initialised = 0; + + return ds; +} + +static inline unsigned _hash_dev(dev_t dev) +{ + return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1); +} + +/* + * Doesn't check that the set already contains dev. + */ +static int _set_insert(struct dev_set *ds, dev_t dev) +{ + struct entry *e; + unsigned h = _hash_dev(dev); + + if (!(e = pool_alloc(ds->mem, sizeof(*e)))) + return 0; + + e->next = ds->slots[h]; + e->dev = dev; + ds->slots[h] = e; + + return 1; +} + +static int _set_lookup(struct dev_set *ds, dev_t dev) +{ + unsigned h = _hash_dev(dev); + struct entry *e; + + for (e = ds->slots[h]; e; e = e->next) + if (e->dev == dev) + return 1; + + return 0; +} + +/*---------------------------------------------------------------- + * filter methods + *--------------------------------------------------------------*/ +static int _parse_dev(const char *file, FILE *fp, dev_t *result) +{ + unsigned major, minor; + char buffer[64]; + + if (!fgets(buffer, sizeof(buffer), fp)) { + log_error("Empty sysfs device file: %s", file); + return 0; + } + + if (sscanf(buffer, "%u:%u", &major, &minor) != 2) { + log_info("sysfs device file not correct format"); + return 0; + } + + *result = makedev(major, minor); + return 1; +} + +static int _read_dev(const char *file, dev_t *result) +{ + int r; + FILE *fp; + + if (!(fp = fopen(file, "r"))) { + log_sys_error("fopen", file); + return 0; + } + + r = _parse_dev(file, fp, result); + fclose(fp); + + return r; +} + +/* + * Recurse through sysfs directories, inserting any devs found. + */ +static int _read_devs(struct dev_set *ds, const char *dir) +{ + struct dirent *d; + DIR *dr; + char path[PATH_MAX]; + dev_t dev; + int r = 1; + + if (!(dr = opendir(dir))) { + log_sys_error("opendir", dir); + return 0; + } + + while ((d = readdir(dr))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + if (lvm_snprintf(path, sizeof(path), "%s/%s", dir, + d->d_name) < 0) { + log_error("sysfs path name too long: %s in %s", + d->d_name, dir); + continue; + } + + if (d->d_type == DT_DIR) { + if (!_read_devs(ds, path)) { + r = 0; + break; + } + } + + if ((d->d_type == DT_REG && !strcmp(d->d_name, "dev"))) + if (!_read_dev(path, &dev) || !_set_insert(ds, dev)) { + r = 0; + break; + } + + } + + if (closedir(dr)) + log_sys_error("closedir", dir); + + return r; +} + +static int _init_devs(struct dev_set *ds) +{ + if (!_read_devs(ds, ds->sys_block)) { + ds->initialised = -1; + return 0; + } + + ds->initialised = 1; + + return 1; +} + + +static int _accept_p(struct dev_filter *f, struct device *dev) +{ + struct dev_set *ds = (struct dev_set *) f->private; + + if (!ds->initialised) + _init_devs(ds); + + /* Pass through if initialisation failed */ + if (ds->initialised != 1) + return 1; + + return _set_lookup(ds, dev->dev); +} + +static void _destroy(struct dev_filter *f) +{ + struct dev_set *ds = (struct dev_set *) f->private; + pool_destroy(ds->mem); +} + +struct dev_filter *sysfs_filter_create(const char *proc) +{ + char sys_block[PATH_MAX]; + struct pool *mem; + struct dev_set *ds; + struct dev_filter *f; + + if (!_locate_sysfs_blocks(proc, sys_block, sizeof(sys_block))) + return NULL; + + if (!(mem = pool_create(256))) { + log_error("sysfs pool creation failed"); + return NULL; + } + + if (!(ds = _dev_set_create(mem, sys_block))) { + log_error("sysfs dev_set creation failed"); + goto bad; + } + + if (!(f = pool_zalloc(mem, sizeof(*f)))) { + stack; + goto bad; + } + + f->passes_filter = _accept_p; + f->destroy = _destroy; + f->private = ds; + return f; + + bad: + pool_destroy(mem); + return NULL; +} + diff --git a/lib/filters/filter-sysfs.h b/lib/filters/filter-sysfs.h new file mode 100644 index 000000000..2c6f750b1 --- /dev/null +++ b/lib/filters/filter-sysfs.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2004 Red Hat Inc + * + * This file is released under the GPL. + */ + +#ifndef _LVM_FILTER_SYSFS_H +#define _LVM_FILTER_SYSFS_H + +#include "config.h" +#include "dev-cache.h" + +struct dev_filter *sysfs_filter_create(const char *proc); + +#endif