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