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