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