Home | History | Annotate | Line # | Download | only in device
      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