lvm/dm - clean up debug, fix open bug
[dragonfly.git] / contrib / lvm2 / dist / lib / filters / filter.c
1 /*      $NetBSD: filter.c,v 1.1.1.2 2009/12/02 00:26:47 haad Exp $      */
2
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #include "lib.h"
19 #include "dev-cache.h"
20 #include "filter.h"
21 #include "lvm-string.h"
22 #include "config.h"
23 #include "metadata.h"
24 #include "activate.h"
25
26 #include <dirent.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <limits.h>
31
32 #define NUMBER_OF_MAJORS 4096
33
34 /* 0 means LVM won't use this major number. */
35 static int _max_partitions_by_major[NUMBER_OF_MAJORS];
36
37 typedef struct {
38         const char *name;
39         const int max_partitions;
40 } device_info_t;
41
42 static int _md_major = -1;
43 static int _blkext_major = -1;
44 static int _drbd_major = -1;
45 static int _device_mapper_major = -1;
46
47 int md_major(void)
48 {
49         return _md_major;
50 }
51
52 int blkext_major(void)
53 {
54         return _blkext_major;
55 }
56
57 int dev_subsystem_part_major(const struct device *dev)
58 {
59         if (MAJOR(dev->dev) == -1)
60                 return 0;
61
62         if (MAJOR(dev->dev) == _md_major)
63                 return 1;
64
65         if (MAJOR(dev->dev) == _drbd_major)
66                 return 1;
67
68         return 0;
69 }
70
71 const char *dev_subsystem_name(const struct device *dev)
72 {
73         if (MAJOR(dev->dev) == _md_major)
74                 return "MD";
75
76         if (MAJOR(dev->dev) == _drbd_major)
77                 return "DRBD";
78
79         return "";
80 }
81
82 /*
83  * Devices are only checked for partition tables if their minor number
84  * is a multiple of the number corresponding to their type below
85  * i.e. this gives the granularity of whole-device minor numbers.
86  * Use 1 if the device is not partitionable.
87  *
88  * The list can be supplemented with devices/types in the config file.
89  */
90 static const device_info_t device_info[] = {
91         {"ide", 64},            /* IDE disk */
92         {"sd", 16},             /* SCSI disk */
93         {"md", 1},              /* Multiple Disk driver (SoftRAID) */
94         {"mdp", 1},             /* Partitionable MD */
95         {"loop", 1},            /* Loop device */
96         {"dasd", 4},            /* DASD disk (IBM S/390, zSeries) */
97         {"dac960", 8},          /* DAC960 */
98         {"nbd", 16},            /* Network Block Device */
99         {"ida", 16},            /* Compaq SMART2 */
100         {"cciss", 16},          /* Compaq CCISS array */
101         {"ubd", 16},            /* User-mode virtual block device */
102         {"ataraid", 16},        /* ATA Raid */
103         {"drbd", 16},           /* Distributed Replicated Block Device */
104         {"emcpower", 16},       /* EMC Powerpath */
105         {"power2", 16},         /* EMC Powerpath */
106         {"i2o_block", 16},      /* i2o Block Disk */
107         {"iseries/vd", 8},      /* iSeries disks */
108         {"gnbd", 1},            /* Network block device */
109         {"ramdisk", 1},         /* RAM disk */
110         {"aoe", 16},            /* ATA over Ethernet */
111         {"device-mapper", 1},   /* Other mapped devices */
112         {"xvd", 16},            /* Xen virtual block device */
113         {"vdisk", 8},           /* SUN's LDOM virtual block device */
114         {"ps3disk", 16},        /* PlayStation 3 internal disk */
115         {"virtblk", 8},         /* VirtIO disk */
116         {"mmc", 16},            /* MMC block device */
117         {"blkext", 1},          /* Extended device partitions */
118         {NULL, 0}
119 };
120
121 static int _passes_lvm_type_device_filter(struct dev_filter *f __attribute((unused)),
122                                           struct device *dev)
123 {
124         const char *name = dev_name(dev);
125         int ret = 0;
126         uint64_t size;
127
128         /* Is this a recognised device type? */
129         if (!_max_partitions_by_major[MAJOR(dev->dev)]) {
130                 log_debug("%s: Skipping: Unrecognised LVM device type %"
131                           PRIu64, name, (uint64_t) MAJOR(dev->dev));
132                 return 0;
133         }
134
135         /* Skip suspended devices */
136         if (MAJOR(dev->dev) == _device_mapper_major &&
137             ignore_suspended_devices() && !device_is_usable(dev->dev)) {
138                 log_debug("%s: Skipping: Suspended dm device", name);
139                 return 0;
140         }
141
142         /* Check it's accessible */
143         if (!dev_open_flags(dev, O_RDONLY, 0, 1)) {
144                 log_debug("%s: Skipping: open failed", name);
145                 return 0;
146         }
147         
148         /* Check it's not too small */
149         if (!dev_get_size(dev, &size)) {
150                 log_debug("%s: Skipping: dev_get_size failed", name);
151                 goto out;
152         }
153
154         if (size < PV_MIN_SIZE) {
155                 log_debug("%s: Skipping: Too small to hold a PV", name);
156                 goto out;
157         }
158
159         if (is_partitioned_dev(dev)) {
160                 log_debug("%s: Skipping: Partition table signature found",
161                           name);
162                 goto out;
163         }
164
165         ret = 1;
166
167       out:
168         dev_close(dev);
169
170         return ret;
171 }
172
173 static int _scan_proc_dev(const char *proc, const struct config_node *cn)
174 {
175         char line[80];
176         char proc_devices[PATH_MAX];
177         FILE *pd = NULL;
178         int i, j = 0;
179         int line_maj = 0;
180         int blocksection = 0;
181         size_t dev_len = 0;
182         struct config_value *cv;
183         char *name;
184
185
186         if (!*proc) {
187                 log_verbose("No proc filesystem found: using all block device "
188                             "types");
189                 for (i = 0; i < NUMBER_OF_MAJORS; i++)
190                         _max_partitions_by_major[i] = 1;
191                 return 1;
192         }
193
194         /* All types unrecognised initially */
195         memset(_max_partitions_by_major, 0, sizeof(int) * NUMBER_OF_MAJORS);
196
197         if (dm_snprintf(proc_devices, sizeof(proc_devices),
198                          "%s/devices", proc) < 0) {
199                 log_error("Failed to create /proc/devices string");
200                 return 0;
201         }
202
203         if (!(pd = fopen(proc_devices, "r"))) {
204                 log_sys_error("fopen", proc_devices);
205                 return 0;
206         }
207
208         while (fgets(line, 80, pd) != NULL) {
209                 i = 0;
210                 while (line[i] == ' ' && line[i] != '\0')
211                         i++;
212
213                 /* If it's not a number it may be name of section */
214                 line_maj = atoi(((char *) (line + i)));
215                 if (!line_maj) {
216                         blocksection = (line[i] == 'B') ? 1 : 0;
217                         continue;
218                 }
219
220                 /* We only want block devices ... */
221                 if (!blocksection)
222                         continue;
223
224                 /* Find the start of the device major name */
225                 while (line[i] != ' ' && line[i] != '\0')
226                         i++;
227                 while (line[i] == ' ' && line[i] != '\0')
228                         i++;
229
230                 /* Look for md device */
231                 if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
232                         _md_major = line_maj;
233
234                 /* Look for blkext device */
235                 if (!strncmp("blkext", line + i, 6) && isspace(*(line + i + 6)))
236                         _blkext_major = line_maj;
237
238                 /* Look for drbd device */
239                 if (!strncmp("drbd", line + i, 4) && isspace(*(line + i + 4)))
240                         _drbd_major = line_maj;
241
242                 /* Look for device-mapper device */
243                 /* FIXME Cope with multiple majors */
244                 if (!strncmp("device-mapper", line + i, 13) && isspace(*(line + i + 13)))
245                         _device_mapper_major = line_maj;
246
247                 /* Go through the valid device names and if there is a
248                    match store max number of partitions */
249                 for (j = 0; device_info[j].name != NULL; j++) {
250                         dev_len = strlen(device_info[j].name);
251                         if (dev_len <= strlen(line + i) &&
252                             !strncmp(device_info[j].name, line + i, dev_len) &&
253                             (line_maj < NUMBER_OF_MAJORS)) {
254                                 _max_partitions_by_major[line_maj] =
255                                     device_info[j].max_partitions;
256                                 break;
257                         }
258                 }
259
260                 if (!cn)
261                         continue;
262
263                 /* Check devices/types for local variations */
264                 for (cv = cn->v; cv; cv = cv->next) {
265                         if (cv->type != CFG_STRING) {
266                                 log_error("Expecting string in devices/types "
267                                           "in config file");
268                                 if (fclose(pd))
269                                         log_sys_error("fclose", proc_devices);
270                                 return 0;
271                         }
272                         dev_len = strlen(cv->v.str);
273                         name = cv->v.str;
274                         cv = cv->next;
275                         if (!cv || cv->type != CFG_INT) {
276                                 log_error("Max partition count missing for %s "
277                                           "in devices/types in config file",
278                                           name);
279                                 if (fclose(pd))
280                                         log_sys_error("fclose", proc_devices);
281                                 return 0;
282                         }
283                         if (!cv->v.i) {
284                                 log_error("Zero partition count invalid for "
285                                           "%s in devices/types in config file",
286                                           name);
287                                 if (fclose(pd))
288                                         log_sys_error("fclose", proc_devices);
289                                 return 0;
290                         }
291                         if (dev_len <= strlen(line + i) &&
292                             !strncmp(name, line + i, dev_len) &&
293                             (line_maj < NUMBER_OF_MAJORS)) {
294                                 _max_partitions_by_major[line_maj] = cv->v.i;
295                                 break;
296                         }
297                 }
298         }
299
300         if (fclose(pd))
301                 log_sys_error("fclose", proc_devices);
302
303         return 1;
304 }
305
306 int max_partitions(int major)
307 {
308         return _max_partitions_by_major[major];
309 }
310
311 struct dev_filter *lvm_type_filter_create(const char *proc,
312                                           const struct config_node *cn)
313 {
314         struct dev_filter *f;
315
316         if (!(f = dm_malloc(sizeof(struct dev_filter)))) {
317                 log_error("LVM type filter allocation failed");
318                 return NULL;
319         }
320
321         f->passes_filter = _passes_lvm_type_device_filter;
322         f->destroy = lvm_type_filter_destroy;
323         f->private = NULL;
324
325         if (!_scan_proc_dev(proc, cn)) {
326                 dm_free(f);
327                 return_NULL;
328         }
329
330         return f;
331 }
332
333 void lvm_type_filter_destroy(struct dev_filter *f)
334 {
335         dm_free(f);
336         return;
337 }