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