xref: /dragonfly/contrib/lvm2/dist/lib/device/device.c (revision 0ca59c34)
1 /*	$NetBSD: device.c,v 1.1.1.2 2009/12/02 00:26:34 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 <libgen.h> /* dirname, basename */
19 #include "lib.h"
20 #include "lvm-types.h"
21 #include "device.h"
22 #include "metadata.h"
23 #include "filter.h"
24 #include "xlate.h"
25 
26 /* See linux/genhd.h and fs/partitions/msdos */
27 
28 #define PART_MAGIC 0xAA55
29 #define PART_MAGIC_OFFSET UINT64_C(0x1FE)
30 #define PART_OFFSET UINT64_C(0x1BE)
31 
32 struct partition {
33 	uint8_t boot_ind;
34 	uint8_t head;
35 	uint8_t sector;
36 	uint8_t cyl;
37 	uint8_t sys_ind;	/* partition type */
38 	uint8_t end_head;
39 	uint8_t end_sector;
40 	uint8_t end_cyl;
41 	uint32_t start_sect;
42 	uint32_t nr_sects;
43 } __attribute__((packed));
44 
45 static int _is_partitionable(struct device *dev)
46 {
47 	int parts = max_partitions(MAJOR(dev->dev));
48 
49 	/* All MD devices are partitionable via blkext (as of 2.6.28) */
50 	if (MAJOR(dev->dev) == md_major())
51 		return 1;
52 
53 	if ((parts <= 1) || (MINOR(dev->dev) % parts))
54 		return 0;
55 
56 	return 1;
57 }
58 
59 static int _has_partition_table(struct device *dev)
60 {
61 	int ret = 0;
62 	unsigned p;
63 	uint16_t buf[SECTOR_SIZE/sizeof(uint16_t)];
64 	uint16_t *part_magic;
65 	struct partition *part;
66 
67 	if (!dev_open(dev)) {
68 		stack;
69 		return -1;
70 	}
71 
72 	if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
73 		goto_out;
74 
75 	/* FIXME Check for other types of partition table too */
76 
77 	/* Check for msdos partition table */
78 	part_magic = buf + PART_MAGIC_OFFSET/sizeof(buf[0]);
79 	if ((*part_magic == xlate16(PART_MAGIC))) {
80 		part = (struct partition *) (buf + PART_OFFSET/sizeof(buf[0]));
81 		for (p = 0; p < 4; p++, part++) {
82 			/* Table is invalid if boot indicator not 0 or 0x80 */
83 			if ((part->boot_ind & 0x7f)) {
84 				ret = 0;
85 				break;
86 			}
87 			/* Must have at least one non-empty partition */
88 			if (part->nr_sects)
89 				ret = 1;
90 		}
91 	}
92 
93       out:
94 	if (!dev_close(dev))
95 		stack;
96 
97 	return ret;
98 }
99 
100 int is_partitioned_dev(struct device *dev)
101 {
102 	if (!_is_partitionable(dev))
103 		return 0;
104 
105 	return _has_partition_table(dev);
106 }
107 
108 #if 0
109 #include <sys/stat.h>
110 #include <sys/mman.h>
111 #include <stdio.h>
112 #include <unistd.h>
113 #include <fcntl.h>
114 #include <ctype.h>
115 
116 #include <errno.h>
117 #include <sys/ioctl.h>
118 #include <linux/fs.h>
119 #include <linux/major.h>
120 #include <linux/genhd.h>
121 
122 int _get_partition_type(struct dev_filter *filter, struct device *d);
123 
124 #define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
125 
126 int is_extended_partition(struct device *d)
127 {
128 	return (MINOR_PART(d) > 4) ? 1 : 0;
129 }
130 
131 struct device *dev_primary(struct dev_mgr *dm, struct device *d)
132 {
133 	struct device *ret;
134 
135 	ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
136 	/* FIXME: Needs replacing with a 'refresh' */
137 	if (!ret) {
138 		init_dev_scan(dm);
139 		ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
140 	}
141 
142 	return ret;
143 
144 }
145 
146 int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
147 {
148 	int pt;
149 
150 	pt = _get_partition_type(dm, d);
151 
152 	if (!pt) {
153 		if (is_whole_disk(dm, d))
154 			/* FIXME: Overloaded pt=0 in error cases */
155 			return 1;
156 		else {
157 			log_error
158 			    ("%s: missing partition table "
159 			     "on partitioned device", d->name);
160 			return 0;
161 		}
162 	}
163 
164 	if (is_whole_disk(dm, d)) {
165 		log_error("%s: looks to possess partition table", d->name);
166 		return 0;
167 	}
168 
169 	/* check part type */
170 	if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
171 		log_error("%s: invalid partition type 0x%x "
172 			  "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
173 		return 0;
174 	}
175 
176 	if (pt == LVM_PARTITION) {
177 		log_error
178 		    ("%s: old LVM partition type found - please change to 0x%x",
179 		     d->name, LVM_NEW_PARTITION);
180 		return 0;
181 	}
182 
183 	return 1;
184 }
185 
186 int _get_partition_type(struct dev_mgr *dm, struct device *d)
187 {
188 	int pv_handle = -1;
189 	struct device *primary;
190 	ssize_t read_ret;
191 	ssize_t bytes_read = 0;
192 	char *buffer;
193 	unsigned short *s_buffer;
194 	struct partition *part;
195 	loff_t offset = 0;
196 	loff_t extended_offset = 0;
197 	int part_sought;
198 	int part_found = 0;
199 	int first_partition = 1;
200 	int extended_partition = 0;
201 	int p;
202 
203 	if (!(primary = dev_primary(dm, d))) {
204 		log_error
205 		    ("Failed to find main device containing partition %s",
206 		     d->name);
207 		return 0;
208 	}
209 
210 	if (!(buffer = dm_malloc(SECTOR_SIZE))) {
211 		log_error("Failed to allocate partition table buffer");
212 		return 0;
213 	}
214 
215 	/* Get partition table */
216 	if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
217 		log_error("%s: open failed: %s", primary->name,
218 			  strerror(errno));
219 		return 0;
220 	}
221 
222 	s_buffer = (unsigned short *) buffer;
223 	part = (struct partition *) (buffer + 0x1be);
224 	part_sought = MINOR_PART(dm, d);
225 
226 	do {
227 		bytes_read = 0;
228 
229 		if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
230 			log_error("%s: llseek failed: %s",
231 				  primary->name, strerror(errno));
232 			return 0;
233 		}
234 
235 		while ((bytes_read < SECTOR_SIZE) &&
236 		       (read_ret =
237 			read(pv_handle, buffer + bytes_read,
238 			     SECTOR_SIZE - bytes_read)) != -1)
239 			bytes_read += read_ret;
240 
241 		if (read_ret == -1) {
242 			log_error("%s: read failed: %s", primary->name,
243 				  strerror(errno));
244 			return 0;
245 		}
246 
247 		if (s_buffer[255] == 0xAA55) {
248 			if (is_whole_disk(dm, d))
249 				return -1;
250 		} else
251 			return 0;
252 
253 		extended_partition = 0;
254 
255 		/* Loop through primary partitions */
256 		for (p = 0; p < 4; p++) {
257 			if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
258 			    part[p].sys_ind == LINUX_EXTENDED_PARTITION
259 			    || part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
260 				extended_partition = 1;
261 				offset = extended_offset + part[p].start_sect;
262 				if (extended_offset == 0)
263 					extended_offset = part[p].start_sect;
264 				if (first_partition == 1)
265 					part_found++;
266 			} else if (first_partition == 1) {
267 				if (p == part_sought) {
268 					if (part[p].sys_ind == 0) {
269 						/* missing primary? */
270 						return 0;
271 					}
272 				} else
273 					part_found++;
274 			} else if (!part[p].sys_ind)
275 				part_found++;
276 
277 			if (part_sought == part_found)
278 				return part[p].sys_ind;
279 
280 		}
281 		first_partition = 0;
282 	}
283 	while (extended_partition == 1);
284 
285 	return 0;
286 }
287 #endif
288 
289 #ifdef linux
290 
291 int get_primary_dev(const char *sysfs_dir,
292 		    struct device *dev, dev_t *result)
293 {
294 	char path[PATH_MAX+1];
295 	char temp_path[PATH_MAX+1];
296 	char buffer[64];
297 	struct stat info;
298 	FILE *fp;
299 	uint32_t pri_maj, pri_min;
300 	int ret = 0;
301 
302 	/* check if dev is a partition */
303 	if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/partition",
304 			sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
305 		log_error("dm_snprintf partition failed");
306 		return ret;
307 	}
308 
309 	if (stat(path, &info) == -1) {
310 		if (errno != ENOENT)
311 			log_sys_error("stat", path);
312 		return ret;
313 	}
314 
315 	/*
316 	 * extract parent's path from the partition's symlink, e.g.:
317 	 * - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1
318 	 * - dirname ../../block/md0/md0p1 = ../../block/md0
319 	 * - basename ../../block/md0/md0  = md0
320 	 * Parent's 'dev' sysfs attribute  = /sys/block/md0/dev
321 	 */
322 	if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
323 		log_sys_error("readlink", path);
324 		return ret;
325 	}
326 
327 	if (dm_snprintf(path, PATH_MAX, "%s/block/%s/dev",
328 			sysfs_dir, basename(dirname(temp_path))) < 0) {
329 		log_error("dm_snprintf dev failed");
330 		return ret;
331 	}
332 
333 	/* finally, parse 'dev' attribute and create corresponding dev_t */
334 	if (stat(path, &info) == -1) {
335 		if (errno == ENOENT)
336 			log_error("sysfs file %s does not exist", path);
337 		else
338 			log_sys_error("stat", path);
339 		return ret;
340 	}
341 
342 	fp = fopen(path, "r");
343 	if (!fp) {
344 		log_sys_error("fopen", path);
345 		return ret;
346 	}
347 
348 	if (!fgets(buffer, sizeof(buffer), fp)) {
349 		log_sys_error("fgets", path);
350 		goto out;
351 	}
352 
353 	if (sscanf(buffer, "%d:%d", &pri_maj, &pri_min) != 2) {
354 		log_error("sysfs file %s not in expected MAJ:MIN format: %s",
355 			  path, buffer);
356 		goto out;
357 	}
358 	*result = MKDEV(pri_maj, pri_min);
359 	ret = 1;
360 
361 out:
362 	if (fclose(fp))
363 		log_sys_error("fclose", path);
364 
365 	return ret;
366 }
367 
368 static unsigned long _dev_topology_attribute(const char *attribute,
369 					     const char *sysfs_dir,
370 					     struct device *dev)
371 {
372 	const char *sysfs_fmt_str = "%s/dev/block/%d:%d/%s";
373 	char path[PATH_MAX+1], buffer[64];
374 	FILE *fp;
375 	struct stat info;
376 	dev_t uninitialized_var(primary);
377 	unsigned long result = 0UL;
378 
379 	if (!attribute || !*attribute)
380 		return_0;
381 
382 	if (!sysfs_dir || !*sysfs_dir)
383 		return_0;
384 
385 	if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
386 			(int)MAJOR(dev->dev), (int)MINOR(dev->dev),
387 			attribute) < 0) {
388 		log_error("dm_snprintf %s failed", attribute);
389 		return 0;
390 	}
391 
392 	/*
393 	 * check if the desired sysfs attribute exists
394 	 * - if not: either the kernel doesn't have topology support
395 	 *   or the device could be a partition
396 	 */
397 	if (stat(path, &info) == -1) {
398 		if (errno != ENOENT) {
399 			log_sys_error("stat", path);
400 			return 0;
401 		}
402 		if (!get_primary_dev(sysfs_dir, dev, &primary))
403 			return 0;
404 
405 		/* get attribute from partition's primary device */
406 		if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
407 				(int)MAJOR(primary), (int)MINOR(primary),
408 				attribute) < 0) {
409 			log_error("primary dm_snprintf %s failed", attribute);
410 			return 0;
411 		}
412 		if (stat(path, &info) == -1) {
413 			if (errno != ENOENT)
414 				log_sys_error("stat", path);
415 			return 0;
416 		}
417 	}
418 
419 	if (!(fp = fopen(path, "r"))) {
420 		log_sys_error("fopen", path);
421 		return 0;
422 	}
423 
424 	if (!fgets(buffer, sizeof(buffer), fp)) {
425 		log_sys_error("fgets", path);
426 		goto out;
427 	}
428 
429 	if (sscanf(buffer, "%lu", &result) != 1) {
430 		log_error("sysfs file %s not in expected format: %s", path,
431 			  buffer);
432 		goto out;
433 	}
434 
435 	log_very_verbose("Device %s %s is %lu bytes.",
436 			 dev_name(dev), attribute, result);
437 
438 out:
439 	if (fclose(fp))
440 		log_sys_error("fclose", path);
441 
442 	return result >> SECTOR_SHIFT;
443 }
444 
445 unsigned long dev_alignment_offset(const char *sysfs_dir,
446 				   struct device *dev)
447 {
448 	return _dev_topology_attribute("alignment_offset",
449 				       sysfs_dir, dev);
450 }
451 
452 unsigned long dev_minimum_io_size(const char *sysfs_dir,
453 				  struct device *dev)
454 {
455 	return _dev_topology_attribute("queue/minimum_io_size",
456 				       sysfs_dir, dev);
457 }
458 
459 unsigned long dev_optimal_io_size(const char *sysfs_dir,
460 				  struct device *dev)
461 {
462 	return _dev_topology_attribute("queue/optimal_io_size",
463 				       sysfs_dir, dev);
464 }
465 
466 #else
467 
468 int get_primary_dev(const char *sysfs_dir,
469 		    struct device *dev, dev_t *result)
470 {
471 	return 0;
472 }
473 
474 unsigned long dev_alignment_offset(const char *sysfs_dir,
475 				   struct device *dev)
476 {
477 	return 0UL;
478 }
479 
480 unsigned long dev_minimum_io_size(const char *sysfs_dir,
481 				  struct device *dev)
482 {
483 	return 0UL;
484 }
485 
486 unsigned long dev_optimal_io_size(const char *sysfs_dir,
487 				  struct device *dev)
488 {
489 	return 0UL;
490 }
491 
492 #endif
493