xref: /dragonfly/contrib/lvm2/dist/lib/filters/filter.c (revision c37c9ab3)
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 }
338