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
_locate_sysfs_blocks(const char * sysfs_dir,char * path,size_t len,unsigned * sysfs_depth)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
_dev_set_create(struct dm_pool * mem,const char * sys_block,unsigned sysfs_depth)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
_hash_dev(dev_t dev)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 */
_set_insert(struct dev_set * ds,dev_t dev)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
_set_lookup(struct dev_set * ds,dev_t dev)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 *--------------------------------------------------------------*/
_parse_dev(const char * file,FILE * fp,dev_t * result)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
_read_dev(const char * file,dev_t * result)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 */
_read_devs(struct dev_set * ds,const char * dir,unsigned sysfs_depth)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
_init_devs(struct dev_set * ds)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
_accept_p(struct dev_filter * f,struct device * dev)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
_destroy(struct dev_filter * f)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
sysfs_filter_create(const char * sysfs_dir)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
sysfs_filter_create(const char * sysfs_dir __attribute ((unused)))331 struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute((unused)))
332 {
333 return NULL;
334 }
335
336 #endif
337