1/*	$NetBSD$	*/
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. */
35static int _max_partitions_by_major[NUMBER_OF_MAJORS];
36
37typedef struct {
38	const char *name;
39	const int max_partitions;
40} device_info_t;
41
42static int _md_major = -1;
43static int _blkext_major = -1;
44static int _drbd_major = -1;
45static int _device_mapper_major = -1;
46
47int md_major(void)
48{
49	return _md_major;
50}
51
52int blkext_major(void)
53{
54	return _blkext_major;
55}
56
57int 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
71const 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 */
90static 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
121static 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
173static 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
306int max_partitions(int major)
307{
308	return _max_partitions_by_major[major];
309}
310
311struct 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
333void lvm_type_filter_destroy(struct dev_filter *f)
334{
335	dm_free(f);
336	return;
337}
338