Home | History | Annotate | Line # | Download | only in filters
      1 /*	$NetBSD: filter-sysfs.c,v 1.1.1.1 2008/12/22 00:17:58 haad Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
      5  *
      6  * This file is part of LVM2.
      7  *
      8  * This copyrighted material is made available to anyone wishing to use,
      9  * modify, copy, or redistribute it subject to the terms and conditions
     10  * of the GNU Lesser General Public License v.2.1.
     11  *
     12  * You should have received a copy of the GNU Lesser General Public License
     13  * along with this program; if not, write to the Free Software Foundation,
     14  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     15  */
     16 
     17 #include "lib.h"
     18 #include "filter-sysfs.h"
     19 #include "lvm-string.h"
     20 
     21 #ifdef linux
     22 
     23 #include <dirent.h>
     24 
     25 static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
     26 				unsigned *sysfs_depth)
     27 {
     28 	struct stat info;
     29 
     30 	/*
     31 	 * unified classification directory for all kernel subsystems
     32 	 *
     33 	 * /sys/subsystem/block/devices
     34 	 * |-- sda -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
     35 	 * |-- sda1 -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
     36 	 *  `-- sr0 -> ../../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
     37 	 *
     38 	 */
     39 	if (dm_snprintf(path, len, "%s/%s", sysfs_dir,
     40 			"subsystem/block/devices") >= 0) {
     41 		if (!stat(path, &info)) {
     42 			*sysfs_depth = 0;
     43 			return 1;
     44 		}
     45 	}
     46 
     47 	/*
     48 	 * block subsystem as a class
     49 	 *
     50 	 * /sys/class/block
     51 	 * |-- sda -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
     52 	 * |-- sda1 -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
     53 	 *  `-- sr0 -> ../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
     54 	 *
     55 	 */
     56 	if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "class/block") >= 0) {
     57 		if (!stat(path, &info)) {
     58 			*sysfs_depth = 0;
     59 			return 1;
     60 		}
     61 	}
     62 
     63 	/*
     64 	 * old block subsystem layout with nested directories
     65 	 *
     66 	 * /sys/block/
     67 	 * |-- sda
     68 	 * |   |-- capability
     69 	 * |   |-- dev
     70 	 * ...
     71 	 * |   |-- sda1
     72 	 * |   |   |-- dev
     73 	 * ...
     74 	 * |
     75 	 * `-- sr0
     76 	 *     |-- capability
     77 	 *     |-- dev
     78 	 * ...
     79 	 *
     80 	 */
     81 	if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "block") >= 0) {
     82 		if (!stat(path, &info)) {
     83 			*sysfs_depth = 1;
     84 			return 1;
     85 		}
     86 	}
     87 
     88 	return 0;
     89 }
     90 
     91 /*----------------------------------------------------------------
     92  * We need to store a set of dev_t.
     93  *--------------------------------------------------------------*/
     94 struct entry {
     95 	struct entry *next;
     96 	dev_t dev;
     97 };
     98 
     99 #define SET_BUCKETS 64
    100 struct dev_set {
    101 	struct dm_pool *mem;
    102 	const char *sys_block;
    103 	unsigned sysfs_depth;
    104 	int initialised;
    105 	struct entry *slots[SET_BUCKETS];
    106 };
    107 
    108 static struct dev_set *_dev_set_create(struct dm_pool *mem,
    109 				       const char *sys_block,
    110 				       unsigned sysfs_depth)
    111 {
    112 	struct dev_set *ds;
    113 
    114 	if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
    115 		return NULL;
    116 
    117 	ds->mem = mem;
    118 	ds->sys_block = dm_pool_strdup(mem, sys_block);
    119 	ds->sysfs_depth = sysfs_depth;
    120 	ds->initialised = 0;
    121 
    122 	return ds;
    123 }
    124 
    125 static unsigned _hash_dev(dev_t dev)
    126 {
    127 	return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
    128 }
    129 
    130 /*
    131  * Doesn't check that the set already contains dev.
    132  */
    133 static int _set_insert(struct dev_set *ds, dev_t dev)
    134 {
    135 	struct entry *e;
    136 	unsigned h = _hash_dev(dev);
    137 
    138 	if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
    139 		return 0;
    140 
    141 	e->next = ds->slots[h];
    142 	e->dev = dev;
    143 	ds->slots[h] = e;
    144 
    145 	return 1;
    146 }
    147 
    148 static int _set_lookup(struct dev_set *ds, dev_t dev)
    149 {
    150 	unsigned h = _hash_dev(dev);
    151 	struct entry *e;
    152 
    153 	for (e = ds->slots[h]; e; e = e->next)
    154 		if (e->dev == dev)
    155 			return 1;
    156 
    157 	return 0;
    158 }
    159 
    160 /*----------------------------------------------------------------
    161  * filter methods
    162  *--------------------------------------------------------------*/
    163 static int _parse_dev(const char *file, FILE *fp, dev_t *result)
    164 {
    165 	unsigned major, minor;
    166 	char buffer[64];
    167 
    168 	if (!fgets(buffer, sizeof(buffer), fp)) {
    169 		log_error("Empty sysfs device file: %s", file);
    170 		return 0;
    171 	}
    172 
    173 	if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
    174 		log_info("sysfs device file not correct format");
    175 		return 0;
    176 	}
    177 
    178 	*result = makedev(major, minor);
    179 	return 1;
    180 }
    181 
    182 static int _read_dev(const char *file, dev_t *result)
    183 {
    184 	int r;
    185 	FILE *fp;
    186 
    187 	if (!(fp = fopen(file, "r"))) {
    188 		log_sys_error("fopen", file);
    189 		return 0;
    190 	}
    191 
    192 	r = _parse_dev(file, fp, result);
    193 
    194 	if (fclose(fp))
    195 		log_sys_error("fclose", file);
    196 
    197 	return r;
    198 }
    199 
    200 /*
    201  * Recurse through sysfs directories, inserting any devs found.
    202  */
    203 static int _read_devs(struct dev_set *ds, const char *dir, unsigned sysfs_depth)
    204 {
    205 	struct dirent *d;
    206 	DIR *dr;
    207 	struct stat info;
    208 	char path[PATH_MAX];
    209 	char file[PATH_MAX];
    210 	dev_t dev = { 0 };
    211 	int r = 1;
    212 
    213 	if (!(dr = opendir(dir))) {
    214 		log_sys_error("opendir", dir);
    215 		return 0;
    216 	}
    217 
    218 	while ((d = readdir(dr))) {
    219 		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
    220 			continue;
    221 
    222 		if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
    223 				 d->d_name) < 0) {
    224 			log_error("sysfs path name too long: %s in %s",
    225 				  d->d_name, dir);
    226 			continue;
    227 		}
    228 
    229 		/* devices have a "dev" file */
    230 		if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
    231 			log_error("sysfs path name too long: %s in %s",
    232 				  d->d_name, dir);
    233 			continue;
    234 		}
    235 
    236 		if (!stat(file, &info)) {
    237 			/* recurse if we found a device and expect subdirs */
    238 			if (sysfs_depth)
    239 				_read_devs(ds, path, sysfs_depth - 1);
    240 
    241 			/* add the device we have found */
    242 			if (_read_dev(file, &dev))
    243 				_set_insert(ds, dev);
    244 		}
    245 	}
    246 
    247 	if (closedir(dr))
    248 		log_sys_error("closedir", dir);
    249 
    250 	return r;
    251 }
    252 
    253 static int _init_devs(struct dev_set *ds)
    254 {
    255 	if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
    256 		ds->initialised = -1;
    257 		return 0;
    258 	}
    259 
    260 	ds->initialised = 1;
    261 
    262 	return 1;
    263 }
    264 
    265 
    266 static int _accept_p(struct dev_filter *f, struct device *dev)
    267 {
    268 	struct dev_set *ds = (struct dev_set *) f->private;
    269 
    270 	if (!ds->initialised)
    271 		_init_devs(ds);
    272 
    273 	/* Pass through if initialisation failed */
    274 	if (ds->initialised != 1)
    275 		return 1;
    276 
    277 	if (!_set_lookup(ds, dev->dev)) {
    278 		log_debug("%s: Skipping (sysfs)", dev_name(dev));
    279 		return 0;
    280 	} else
    281 		return 1;
    282 }
    283 
    284 static void _destroy(struct dev_filter *f)
    285 {
    286 	struct dev_set *ds = (struct dev_set *) f->private;
    287 	dm_pool_destroy(ds->mem);
    288 }
    289 
    290 struct dev_filter *sysfs_filter_create(const char *sysfs_dir)
    291 {
    292 	char sys_block[PATH_MAX];
    293 	unsigned sysfs_depth;
    294 	struct dm_pool *mem;
    295 	struct dev_set *ds;
    296 	struct dev_filter *f;
    297 
    298 	if (!*sysfs_dir) {
    299 		log_verbose("No proc filesystem found: skipping sysfs filter");
    300 		return NULL;
    301 	}
    302 
    303 	if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
    304 		return NULL;
    305 
    306 	if (!(mem = dm_pool_create("sysfs", 256))) {
    307 		log_error("sysfs pool creation failed");
    308 		return NULL;
    309 	}
    310 
    311 	if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
    312 		log_error("sysfs dev_set creation failed");
    313 		goto bad;
    314 	}
    315 
    316 	if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
    317 		goto_bad;
    318 
    319 	f->passes_filter = _accept_p;
    320 	f->destroy = _destroy;
    321 	f->private = ds;
    322 	return f;
    323 
    324  bad:
    325 	dm_pool_destroy(mem);
    326 	return NULL;
    327 }
    328 
    329 #else
    330 
    331 struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute((unused)))
    332 {
    333 	return NULL;
    334 }
    335 
    336 #endif
    337