1 /*      $NetBSD: filter_netbsd.c,v 1.3 2009/12/02 01:53:25 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  * Copyright (C) 2008 Adam Hamsik. All rights reserved.
7  *
8  * This file is part of LVM2.
9  *
10  * This copyrighted material is made available to anyone wishing to use,
11  * modify, copy, or redistribute it subject to the terms and conditions
12  * of the GNU Lesser General Public License v.2.1.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include "lib.h"
20 #include "dev-cache.h"
21 #include "filter.h"
22 #include "lvm-string.h"
23 #include "config.h"
24 #include "metadata.h"
25 #include "activate.h"
26 
27 #include <sys/sysctl.h>
28 
29 #include <ctype.h>
30 #include <dirent.h>
31 #include <fcntl.h>
32 #include <limits.h>
33 #include <unistd.h>
34 
35 #define NUMBER_OF_MAJORS 4096
36 
37 #define LVM_SUCCESS 1
38 #define LVM_FAILURE 0
39 
40 /* -1 means LVM won't use this major number. */
41 static int _char_device_major[NUMBER_OF_MAJORS];
42 static int _block_device_major[NUMBER_OF_MAJORS];
43 
44 typedef struct {
45 	const char *name;
46 	const int max_partitions;
47 } device_info_t;
48 
49 static int _md_major = -1;
50 static int _device_mapper_major = -1;
51 
52 int md_major(void)
53 {
54 	return _md_major;
55 }
56 
57 int dev_subsystem_part_major(const struct device *dev)
58 {
59 	return 0;
60 }
61 
62 const char *dev_subsystem_name(const struct device *dev)
63 {
64 	return "";
65 }
66 
67 /*
68  * Devices are only checked for partition tables if their minor number
69  * is a multiple of the number corresponding to their type below
70  * i.e. this gives the granularity of whole-device minor numbers.
71  * Use 1 if the device is not partitionable.
72  *
73  * The list can be supplemented with devices/types in the config file.
74  */
75 static const device_info_t device_info[] = {
76 	{"wd", 64},
77 	{"sd", 64},
78 	{"dk", 1},
79 	{"wd", 64},
80 	{"vnd", 1},
81 	{"raid", 64},
82 	{"cgd", 1},
83 	{"ccd", 1},
84 	{"xbd", 64},
85 	{NULL, -1}
86 };
87 
88 /*
89  * Test if device passes filter tests and can be inserted in to cache.
90  */
91 static int _passes_lvm_type_device_filter(struct dev_filter *f __attribute((unused)),
92 					  struct device *dev)
93 {
94 	const char *name = dev_name(dev);
95 	int ret = 0;
96 	uint64_t size;
97 
98 	/* Is this a recognised device type? */
99 	if (_char_device_major[MAJOR(dev->dev)] == -1 ){
100 		log_debug("%s: Skipping: Unrecognised LVM device type %"
101 				  PRIu64, name, (uint64_t) MAJOR(dev->dev));
102 		return LVM_FAILURE;
103 	}
104 
105 	/* Skip suspended devices */
106 	if (MAJOR(dev->dev) == _device_mapper_major &&
107 	    ignore_suspended_devices() && !device_is_usable(dev->dev)) {
108 		log_debug("%s: Skipping: Suspended dm device", name);
109 		return LVM_FAILURE;
110 	}
111 
112 	/* Check it's accessible */
113 	if (!dev_open_flags(dev, O_RDONLY, 0, 1)) {
114 		log_debug("%s: Skipping: open failed", name);
115 		return LVM_FAILURE;
116 	}
117 
118 	/* Check it's not too small */
119 	if (!dev_get_size(dev, &size)) {
120 	       	log_debug("%s: Skipping: dev_get_size failed", name);
121 		goto out;
122 	}
123 
124 	if (size < PV_MIN_SIZE) {
125 		log_debug("%s: Skipping: Too small to hold a PV", name);
126 		goto out;
127 	}
128 
129 	if (is_partitioned_dev(dev)) {
130 		log_debug("%s: Skipping: Partition table signature found",
131 			  name);
132 		goto out;
133 	}
134 
135 	ret = LVM_SUCCESS;
136 
137       out:
138 	dev_close(dev);
139 
140 	return ret;
141 }
142 
143 static int _scan_dev(const struct config_node *cn)
144 {
145 	size_t val_len,i,j;
146 	char *name;
147 
148 	struct kinfo_drivers *kd;
149 	struct config_value *cv;
150 
151 	/* All types unrecognised initially */
152 	memset(_char_device_major, -1, sizeof(int) * NUMBER_OF_MAJORS);
153 	memset(_block_device_major, -1, sizeof(int) * NUMBER_OF_MAJORS);
154 
155 	/* get size kernel drivers array from kernel*/
156 	if (sysctlbyname("kern.drivers", NULL, &val_len, NULL, 0) < 0) {
157 		printf("sysctlbyname failed");
158 		return LVM_FAILURE;
159 	}
160 
161 	if ((kd = malloc(val_len)) == NULL){
162 		printf("malloc kd info error\n");
163 		return LVM_FAILURE;
164 	}
165 
166 	/* get array from kernel */
167 	if (sysctlbyname("kern.drivers", kd, &val_len, NULL, 0) < 0) {
168 		printf("sysctlbyname failed kd");
169 		return LVM_FAILURE;
170 	}
171 
172 	for (i = 0, val_len /= sizeof(*kd); i < val_len; i++) {
173 
174 		if (!strncmp("device-mapper", kd[i].d_name, 13) ||
175 		    !strncmp("dm", kd[i].d_name, 2))
176 			_device_mapper_major = kd[i].d_bmajor;
177 
178 		/* We select only devices with correct char/block major number. */
179 		if (kd[i].d_cmajor != -1 && kd[i].d_bmajor != -1) {
180 			/* Go through the valid device names and if there is a
181 			   match store max number of partitions */
182 			for (j = 0; device_info[j].name != NULL; j++){
183 				if (!strcmp(device_info[j].name, kd[i].d_name)){
184 					_char_device_major[kd[i].d_cmajor] =
185 					    device_info[j].max_partitions;
186 					_block_device_major[kd[i].d_bmajor] =
187 					    device_info[j].max_partitions;
188 					break;
189 				}
190 			}
191 		}
192 
193 		if (!cn)
194 			continue;
195 
196 		/* Check devices/types for local variations */
197 		for (cv = cn->v; cv; cv = cv->next) {
198 			if (cv->type != CFG_STRING) {
199 				log_error("Expecting string in devices/types "
200 					  "in config file");
201 				free(kd);
202 				return LVM_FAILURE;
203 			}
204 
205 			name = cv->v.str;
206 			cv = cv->next;
207 			if (!cv || cv->type != CFG_INT) {
208 				log_error("Max partition count missing for %s "
209 					  "in devices/types in config file",
210 					  name);
211 				free(kd);
212 				return LVM_FAILURE;
213 			}
214 			if (!cv->v.i) {
215 				log_error("Zero partition count invalid for "
216 					  "%s in devices/types in config file",
217 					  name);
218 				free(kd);
219 				return LVM_FAILURE;
220 			}
221 
222 			if (!strncmp(name, kd[i].d_name, strlen(name))){
223 					_char_device_major[kd[i].d_cmajor] =
224 					    device_info[j].max_partitions;
225 					_block_device_major[kd[i].d_bmajor] =
226 					    device_info[j].max_partitions;
227 					break;
228 			}
229 		}
230 	}
231 
232 	free(kd);
233 
234 	return LVM_SUCCESS;
235 }
236 
237 int max_partitions(int major)
238 {
239 	return _char_device_major[major];
240 }
241 
242 struct dev_filter *lvm_type_filter_create(const char *proc,
243 					  const struct config_node *cn)
244 {
245 	struct dev_filter *f;
246 
247 	if (!(f = dm_malloc(sizeof(struct dev_filter)))) {
248 		log_error("LVM type filter allocation failed");
249 		return NULL;
250 	}
251 
252 	f->passes_filter = _passes_lvm_type_device_filter;
253 	f->destroy = lvm_type_filter_destroy;
254 	f->private = NULL;
255 
256 	if (!_scan_dev(cn)) {
257 		dm_free(f);
258 		return_NULL;
259 	}
260 
261 	return f;
262 }
263 
264 void lvm_type_filter_destroy(struct dev_filter *f)
265 {
266 	dm_free(f);
267 	return;
268 }
269