Tizen 2.1 base
[external/device-mapper.git] / lib / filters / filter-sysfs.c
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 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         if (dm_snprintf(path, len, "%s/%s", sysfs_dir,
38                         "subsystem/block/devices") >= 0) {
39                 if (!stat(path, &info)) {
40                         *sysfs_depth = 0;
41                         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         if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "class/block") >= 0) {
55                 if (!stat(path, &info)) {
56                         *sysfs_depth = 0;
57                         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         if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "block") >= 0) {
80                 if (!stat(path, &info)) {
81                         *sysfs_depth = 1;
82                         return 1;
83                 }
84         }
85
86         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 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         if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
113                 return NULL;
114
115         ds->mem = mem;
116         ds->sys_block = dm_pool_strdup(mem, sys_block);
117         ds->sysfs_depth = sysfs_depth;
118         ds->initialised = 0;
119
120         return ds;
121 }
122
123 static unsigned _hash_dev(dev_t dev)
124 {
125         return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
126 }
127
128 /*
129  * Doesn't check that the set already contains dev.
130  */
131 static int _set_insert(struct dev_set *ds, dev_t dev)
132 {
133         struct entry *e;
134         unsigned h = _hash_dev(dev);
135
136         if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
137                 return 0;
138
139         e->next = ds->slots[h];
140         e->dev = dev;
141         ds->slots[h] = e;
142
143         return 1;
144 }
145
146 static int _set_lookup(struct dev_set *ds, dev_t dev)
147 {
148         unsigned h = _hash_dev(dev);
149         struct entry *e;
150
151         for (e = ds->slots[h]; e; e = e->next)
152                 if (e->dev == dev)
153                         return 1;
154
155         return 0;
156 }
157
158 /*----------------------------------------------------------------
159  * filter methods
160  *--------------------------------------------------------------*/
161 static int _parse_dev(const char *file, FILE *fp, dev_t *result)
162 {
163         unsigned major, minor;
164         char buffer[64];
165
166         if (!fgets(buffer, sizeof(buffer), fp)) {
167                 log_error("Empty sysfs device file: %s", file);
168                 return 0;
169         }
170
171         if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
172                 log_info("sysfs device file not correct format");
173                 return 0;
174         }
175
176         *result = makedev(major, minor);
177         return 1;
178 }
179
180 static int _read_dev(const char *file, dev_t *result)
181 {
182         int r;
183         FILE *fp;
184
185         if (!(fp = fopen(file, "r"))) {
186                 log_sys_error("fopen", file);
187                 return 0;
188         }
189
190         r = _parse_dev(file, fp, result);
191
192         if (fclose(fp))
193                 log_sys_error("fclose", file);
194
195         return r;
196 }
197
198 /*
199  * Recurse through sysfs directories, inserting any devs found.
200  */
201 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         dev_t dev = { 0 };
209         int r = 1;
210
211         if (!(dr = opendir(dir))) {
212                 log_sys_error("opendir", dir);
213                 return 0;
214         }
215
216         while ((d = readdir(dr))) {
217                 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
218                         continue;
219
220                 if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
221                                  d->d_name) < 0) {
222                         log_error("sysfs path name too long: %s in %s",
223                                   d->d_name, dir);
224                         continue;
225                 }
226
227                 /* devices have a "dev" file */
228                 if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
229                         log_error("sysfs path name too long: %s in %s",
230                                   d->d_name, dir);
231                         continue;
232                 }
233
234                 if (!stat(file, &info)) {
235                         /* recurse if we found a device and expect subdirs */
236                         if (sysfs_depth)
237                                 _read_devs(ds, path, sysfs_depth - 1);
238
239                         /* add the device we have found */
240                         if (_read_dev(file, &dev))
241                                 _set_insert(ds, dev);
242                 }
243         }
244
245         if (closedir(dr))
246                 log_sys_error("closedir", dir);
247
248         return r;
249 }
250
251 static int _init_devs(struct dev_set *ds)
252 {
253         if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
254                 ds->initialised = -1;
255                 return 0;
256         }
257
258         ds->initialised = 1;
259
260         return 1;
261 }
262
263
264 static int _accept_p(struct dev_filter *f, struct device *dev)
265 {
266         struct dev_set *ds = (struct dev_set *) f->private;
267
268         if (!ds->initialised)
269                 _init_devs(ds);
270
271         /* Pass through if initialisation failed */
272         if (ds->initialised != 1)
273                 return 1;
274
275         if (!_set_lookup(ds, dev->dev)) {
276                 log_debug("%s: Skipping (sysfs)", dev_name(dev));
277                 return 0;
278         } else
279                 return 1;
280 }
281
282 static void _destroy(struct dev_filter *f)
283 {
284         struct dev_set *ds = (struct dev_set *) f->private;
285
286         if (f->use_count)
287                 log_error(INTERNAL_ERROR "Destroying sysfs filter while in use %u times.", f->use_count);
288
289         dm_pool_destroy(ds->mem);
290 }
291
292 struct dev_filter *sysfs_filter_create(const char *sysfs_dir)
293 {
294         char sys_block[PATH_MAX];
295         unsigned sysfs_depth;
296         struct dm_pool *mem;
297         struct dev_set *ds;
298         struct dev_filter *f;
299
300         if (!*sysfs_dir) {
301                 log_verbose("No proc filesystem found: skipping sysfs filter");
302                 return NULL;
303         }
304
305         if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
306                 return NULL;
307
308         if (!(mem = dm_pool_create("sysfs", 256))) {
309                 log_error("sysfs pool creation failed");
310                 return NULL;
311         }
312
313         if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
314                 log_error("sysfs dev_set creation failed");
315                 goto bad;
316         }
317
318         if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
319                 goto_bad;
320
321         f->passes_filter = _accept_p;
322         f->destroy = _destroy;
323         f->use_count = 0;
324         f->private = ds;
325         return f;
326
327  bad:
328         dm_pool_destroy(mem);
329         return NULL;
330 }
331
332 #else
333
334 struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute__((unused)))
335 {
336         return NULL;
337 }
338
339 #endif