2 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
4 * This file is part of LVM2.
6 * This copyrighted material is made available to anyone wishing to use,
7 * modify, copy, or redistribute it subject to the terms and conditions
8 * of the GNU Lesser General Public License v.2.1.
10 * You should have received a copy of the GNU Lesser General Public License
11 * along with this program; if not, write to the Free Software Foundation,
12 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 #include "filter-sysfs.h"
17 #include "lvm-string.h"
23 static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
24 unsigned *sysfs_depth)
29 * unified classification directory for all kernel subsystems
31 * /sys/subsystem/block/devices
32 * |-- sda -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
33 * |-- sda1 -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
34 * `-- sr0 -> ../../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
37 if (dm_snprintf(path, len, "%s/%s", sysfs_dir,
38 "subsystem/block/devices") >= 0) {
39 if (!stat(path, &info)) {
46 * block subsystem as a class
49 * |-- sda -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
50 * |-- sda1 -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
51 * `-- sr0 -> ../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
54 if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "class/block") >= 0) {
55 if (!stat(path, &info)) {
62 * old block subsystem layout with nested directories
79 if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "block") >= 0) {
80 if (!stat(path, &info)) {
89 /*----------------------------------------------------------------
90 * We need to store a set of dev_t.
91 *--------------------------------------------------------------*/
97 #define SET_BUCKETS 64
100 const char *sys_block;
101 unsigned sysfs_depth;
103 struct entry *slots[SET_BUCKETS];
106 static struct dev_set *_dev_set_create(struct dm_pool *mem,
107 const char *sys_block,
108 unsigned sysfs_depth)
112 if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
116 ds->sys_block = dm_pool_strdup(mem, sys_block);
117 ds->sysfs_depth = sysfs_depth;
123 static unsigned _hash_dev(dev_t dev)
125 return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
129 * Doesn't check that the set already contains dev.
131 static int _set_insert(struct dev_set *ds, dev_t dev)
134 unsigned h = _hash_dev(dev);
136 if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
139 e->next = ds->slots[h];
146 static int _set_lookup(struct dev_set *ds, dev_t dev)
148 unsigned h = _hash_dev(dev);
151 for (e = ds->slots[h]; e; e = e->next)
158 /*----------------------------------------------------------------
160 *--------------------------------------------------------------*/
161 static int _parse_dev(const char *file, FILE *fp, dev_t *result)
163 unsigned major, minor;
166 if (!fgets(buffer, sizeof(buffer), fp)) {
167 log_error("Empty sysfs device file: %s", file);
171 if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
172 log_info("sysfs device file not correct format");
176 *result = makedev(major, minor);
180 static int _read_dev(const char *file, dev_t *result)
185 if (!(fp = fopen(file, "r"))) {
186 log_sys_error("fopen", file);
190 r = _parse_dev(file, fp, result);
193 log_sys_error("fclose", file);
199 * Recurse through sysfs directories, inserting any devs found.
201 static int _read_devs(struct dev_set *ds, const char *dir, unsigned sysfs_depth)
211 if (!(dr = opendir(dir))) {
212 log_sys_error("opendir", dir);
216 while ((d = readdir(dr))) {
217 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
220 if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
222 log_error("sysfs path name too long: %s in %s",
227 /* devices have a "dev" file */
228 if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
229 log_error("sysfs path name too long: %s in %s",
234 if (!stat(file, &info)) {
235 /* recurse if we found a device and expect subdirs */
237 _read_devs(ds, path, sysfs_depth - 1);
239 /* add the device we have found */
240 if (_read_dev(file, &dev))
241 _set_insert(ds, dev);
246 log_sys_error("closedir", dir);
251 static int _init_devs(struct dev_set *ds)
253 if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
254 ds->initialised = -1;
264 static int _accept_p(struct dev_filter *f, struct device *dev)
266 struct dev_set *ds = (struct dev_set *) f->private;
268 if (!ds->initialised)
271 /* Pass through if initialisation failed */
272 if (ds->initialised != 1)
275 if (!_set_lookup(ds, dev->dev)) {
276 log_debug("%s: Skipping (sysfs)", dev_name(dev));
282 static void _destroy(struct dev_filter *f)
284 struct dev_set *ds = (struct dev_set *) f->private;
287 log_error(INTERNAL_ERROR "Destroying sysfs filter while in use %u times.", f->use_count);
289 dm_pool_destroy(ds->mem);
292 struct dev_filter *sysfs_filter_create(const char *sysfs_dir)
294 char sys_block[PATH_MAX];
295 unsigned sysfs_depth;
298 struct dev_filter *f;
301 log_verbose("No proc filesystem found: skipping sysfs filter");
305 if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
308 if (!(mem = dm_pool_create("sysfs", 256))) {
309 log_error("sysfs pool creation failed");
313 if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
314 log_error("sysfs dev_set creation failed");
318 if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
321 f->passes_filter = _accept_p;
322 f->destroy = _destroy;
328 dm_pool_destroy(mem);
334 struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute__((unused)))