1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 28 /* 29 * This file contains miscellaneous device validation routines. 30 */ 31 32 #include "global.h" 33 #include <sys/mnttab.h> 34 #include <sys/mntent.h> 35 #include <sys/autoconf.h> 36 37 #include <signal.h> 38 #include <malloc.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <libgen.h> 44 #include <sys/ioctl.h> 45 #include <sys/fcntl.h> 46 #include <sys/stat.h> 47 #include <sys/swap.h> 48 #include <sys/sysmacros.h> 49 #include <sys/mkdev.h> 50 #include <sys/modctl.h> 51 #include <ctype.h> 52 #include <libdiskmgt.h> 53 #include <libnvpair.h> 54 #include "misc.h" 55 #include "checkdev.h" 56 #include <sys/efi_partition.h> 57 58 /* Function prototypes */ 59 static struct swaptable *getswapentries(void); 60 static void freeswapentries(struct swaptable *); 61 static int getpartition(char *pathname); 62 static int checkpartitions(int bm_mounted); 63 64 static struct swaptable * 65 getswapentries(void) 66 { 67 struct swaptable *st; 68 struct swapent *swapent; 69 int i, num; 70 char fullpathname[MAXPATHLEN]; 71 72 /* 73 * get the number of swap entries 74 */ 75 if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { 76 err_print("swapctl error "); 77 fullabort(); 78 } 79 if (num == 0) 80 return (NULL); 81 if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int))) 82 == NULL) { 83 err_print("getswapentries: malloc failed.\n"); 84 fullabort(); 85 } 86 swapent = st->swt_ent; 87 for (i = 0; i < num; i++, swapent++) { 88 if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) { 89 err_print("getswapentries: malloc failed.\n"); 90 fullabort(); 91 } 92 } 93 st->swt_n = num; 94 if ((num = swapctl(SC_LIST, (void *)st)) == -1) { 95 err_print("swapctl error "); 96 fullabort(); 97 } 98 swapent = st->swt_ent; 99 for (i = 0; i < num; i++, swapent++) { 100 if (*swapent->ste_path != '/') { 101 (void) snprintf(fullpathname, sizeof (fullpathname), 102 "/dev/%s", swapent->ste_path); 103 (void) strcpy(swapent->ste_path, fullpathname); 104 } 105 } 106 return (st); 107 } 108 109 static void 110 freeswapentries(struct swaptable *st) 111 { 112 struct swapent *swapent; 113 int i; 114 115 swapent = st->swt_ent; 116 for (i = 0; i < st->swt_n; i++, swapent++) 117 free(swapent->ste_path); 118 free(st); 119 120 } 121 122 /* 123 * function getpartition: 124 */ 125 static int 126 getpartition(char *pathname) 127 { 128 int mfd; 129 struct dk_cinfo dkinfo; 130 struct stat stbuf; 131 char raw_device[MAXPATHLEN]; 132 int found = -1; 133 134 /* 135 * Map the block device name to the raw device name. 136 * If it doesn't appear to be a device name, skip it. 137 */ 138 if (match_substr(pathname, "/dev/") == 0) 139 return (found); 140 (void) strcpy(raw_device, "/dev/r"); 141 (void) strcat(raw_device, pathname + strlen("/dev/")); 142 /* 143 * Determine if this appears to be a disk device. 144 * First attempt to open the device. If if fails, skip it. 145 */ 146 if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) { 147 return (found); 148 } 149 /* 150 * Must be a character device 151 */ 152 if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) { 153 (void) close(mfd); 154 return (found); 155 } 156 /* 157 * Attempt to read the configuration info on the disk. 158 */ 159 if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) { 160 (void) close(mfd); 161 return (found); 162 } 163 /* 164 * Finished with the opened device 165 */ 166 (void) close(mfd); 167 168 /* 169 * If it's not the disk we're interested in, it doesn't apply. 170 */ 171 if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype || 172 cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum || 173 cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit || 174 strcmp(cur_disk->disk_dkinfo.dki_dname, dkinfo.dki_dname) != 0) { 175 return (found); 176 } 177 178 /* 179 * Extract the partition that is mounted. 180 */ 181 return (PARTITION(stbuf.st_rdev)); 182 } 183 184 /* 185 * This Routine checks to see if there are partitions used for swapping overlaps 186 * a given portion of a disk. If the start parameter is < 0, it means 187 * that the entire disk should be checked 188 */ 189 int 190 checkswap(diskaddr_t start, diskaddr_t end) 191 { 192 struct swaptable *st; 193 struct swapent *swapent; 194 int i; 195 int found = 0; 196 struct dk_map32 *map; 197 int part; 198 199 /* 200 * If we are only checking part of the disk, the disk must 201 * have a partition map to check against. If it doesn't, 202 * we hope for the best. 203 */ 204 if (cur_parts == NULL) 205 return (0); 206 207 /* 208 * check for swap entries 209 */ 210 st = getswapentries(); 211 /* 212 * if there are no swap entries return. 213 */ 214 if (st == NULL) 215 return (0); 216 swapent = st->swt_ent; 217 for (i = 0; i < st->swt_n; i++, swapent++) { 218 if ((part = getpartition(swapent->ste_path)) != -1) { 219 if (start == UINT_MAX64) { 220 found = -1; 221 break; 222 } 223 map = &cur_parts->pinfo_map[part]; 224 if ((start >= (int)(map->dkl_cylno * spc() + 225 map->dkl_nblk)) || 226 (end < (int)(map->dkl_cylno * spc()))) { 227 continue; 228 } 229 found = -1; 230 break; 231 }; 232 } 233 freeswapentries(st); 234 /* 235 * If we found trouble and we're running from a command file, 236 * quit before doing something we really regret. 237 */ 238 239 if (found && option_f) { 240 err_print( 241 "Operation on disks being used for swapping must be interactive.\n"); 242 cmdabort(SIGINT); 243 } 244 245 return (found); 246 247 248 } 249 /* 250 * Determines if there are partitions that are a part of an SVM, VxVM, zpool 251 * volume or a live upgrade device, overlapping a given portion of a disk. 252 * Mounts and swap devices are checked in legacy format code. 253 */ 254 int 255 checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print, 256 int check_label) 257 { 258 259 int error; 260 int found = 0; 261 int check = 0; 262 int i; 263 int bm_inuse = 0; 264 int part = 0; 265 uint64_t slice_start, slice_size; 266 dm_descriptor_t *slices = NULL; 267 nvlist_t *attrs = NULL; 268 char *usage; 269 char *name; 270 271 /* 272 * If the user does not want to do in use checking, return immediately. 273 * Normally, this is handled in libdiskmgt. For format, there is more 274 * processing required, so we want to bypass the in use checking 275 * here. 276 */ 277 278 if (NOINUSE_SET) 279 return (0); 280 281 /* 282 * Skip if it is not a real disk 283 * 284 * There could be two kinds of strings in cur_disk_path 285 * One starts with c?t?d?, while the other is a absolute path of a 286 * block device file. 287 */ 288 289 if (*cur_disk_path != 'c') { 290 struct stat stbuf; 291 char majorname[16]; 292 major_t majornum; 293 294 (void) stat(cur_disk_path, &stbuf); 295 majornum = major(stbuf.st_rdev); 296 (void) modctl(MODGETNAME, majorname, sizeof (majorname), 297 &majornum); 298 299 if (strcmp(majorname, "sd")) 300 if (strcmp(majorname, "ssd")) 301 if (strcmp(majorname, "cmdk")) 302 return (0); 303 } 304 305 /* 306 * Truncate the characters following "d*", such as "s*" or "p*" 307 */ 308 cur_disk_path = basename(cur_disk_path); 309 name = strrchr(cur_disk_path, 'd'); 310 if (name) { 311 name++; 312 for (; (*name <= '9') && (*name >= '0'); name++) { 313 } 314 *name = (char)0; 315 } 316 317 318 /* 319 * For format, we get basic 'in use' details from libdiskmgt. After 320 * that we must do the appropriate checking to see if the 'in use' 321 * details require a bit of additional work. 322 */ 323 324 dm_get_slices(cur_disk_path, &slices, &error); 325 if (error) { 326 /* 327 * If ENODEV, it actually means the device is not in use. 328 * We will return 0 without displaying error. 329 */ 330 if (error != ENODEV) { 331 err_print("Error occurred with device in use" 332 "checking: %s\n", strerror(error)); 333 return (found); 334 } 335 } 336 if (slices == NULL) 337 return (found); 338 339 for (i = 0; slices[i] != 0; i++) { 340 /* 341 * If we are checking the whole disk 342 * then any and all in use data is 343 * relevant. 344 */ 345 if (start == UINT_MAX64) { 346 name = dm_get_name(slices[i], &error); 347 if (error != 0 || !name) { 348 err_print("Error occurred with device " 349 "in use checking: %s\n", strerror(error)); 350 continue; 351 } 352 if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || 353 error) { 354 if (error != 0) { 355 dm_free_name(name); 356 name = NULL; 357 err_print("Error occurred with " 358 "device in use checking: " 359 "%s\n", strerror(error)); 360 continue; 361 } 362 dm_free_name(name); 363 name = NULL; 364 /* 365 * If this is a dump device, then it is 366 * a failure. You cannot format a slice 367 * that is a dedicated dump device. 368 */ 369 370 if (strstr(usage, DM_USE_DUMP)) { 371 if (print) { 372 err_print(usage); 373 free(usage); 374 } 375 dm_free_descriptors(slices); 376 return (1); 377 } 378 /* 379 * We really found a device that is in use. 380 * Set 'found' for the return value, and set 381 * 'check' to indicate below that we must 382 * get the partition number to set bm_inuse 383 * in the event we are trying to label this 384 * device. check_label is set when we are 385 * checking modifications for in use slices 386 * on the device. 387 */ 388 found ++; 389 check = 1; 390 if (print) { 391 err_print(usage); 392 free(usage); 393 } 394 } 395 } else { 396 /* 397 * Before getting the in use data, verify that the 398 * current slice is within the range we are checking. 399 */ 400 attrs = dm_get_attributes(slices[i], &error); 401 if (error) { 402 err_print("Error occurred with device in use " 403 "checking: %s\n", strerror(error)); 404 continue; 405 } 406 if (attrs == NULL) { 407 continue; 408 } 409 410 (void) nvlist_lookup_uint64(attrs, DM_START, 411 &slice_start); 412 (void) nvlist_lookup_uint64(attrs, DM_SIZE, 413 &slice_size); 414 if (start >= (slice_start + slice_size) || 415 (end < slice_start)) { 416 nvlist_free(attrs); 417 attrs = NULL; 418 continue; 419 } 420 name = dm_get_name(slices[i], &error); 421 if (error != 0 || !name) { 422 err_print("Error occurred with device " 423 "in use checking: %s\n", strerror(error)); 424 nvlist_free(attrs); 425 attrs = NULL; 426 continue; 427 } 428 if (dm_inuse(name, &usage, 429 DM_WHO_FORMAT, &error) || error) { 430 if (error != 0) { 431 dm_free_name(name); 432 name = NULL; 433 err_print("Error occurred with " 434 "device in use checking: " 435 "%s\n", strerror(error)); 436 nvlist_free(attrs); 437 attrs = NULL; 438 continue; 439 } 440 dm_free_name(name); 441 name = NULL; 442 /* 443 * If this is a dump device, then it is 444 * a failure. You cannot format a slice 445 * that is a dedicated dump device. 446 */ 447 if (strstr(usage, DM_USE_DUMP)) { 448 if (print) { 449 err_print(usage); 450 free(usage); 451 } 452 dm_free_descriptors(slices); 453 nvlist_free(attrs); 454 return (1); 455 } 456 /* 457 * We really found a device that is in use. 458 * Set 'found' for the return value, and set 459 * 'check' to indicate below that we must 460 * get the partition number to set bm_inuse 461 * in the event we are trying to label this 462 * device. check_label is set when we are 463 * checking modifications for in use slices 464 * on the device. 465 */ 466 found ++; 467 check = 1; 468 if (print) { 469 err_print(usage); 470 free(usage); 471 } 472 } 473 } 474 /* 475 * If check is set it means we found a slice(the current slice) 476 * on this device in use in some way. We potentially want 477 * to check this slice when labeling is 478 * requested. We set bm_inuse with this partition value 479 * for use later if check_label was set when called. 480 */ 481 if (check) { 482 name = dm_get_name(slices[i], &error); 483 if (error != 0 || !name) { 484 err_print("Error occurred with device " 485 "in use checking: %s\n", strerror(error)); 486 nvlist_free(attrs); 487 attrs = NULL; 488 continue; 489 } 490 part = getpartition(name); 491 dm_free_name(name); 492 name = NULL; 493 if (part != -1) { 494 bm_inuse |= 1 << part; 495 } 496 check = 0; 497 } 498 /* 499 * If we have attributes then we have successfully 500 * found the slice we were looking for and we also 501 * know this means we are not searching the whole 502 * disk so break out of the loop 503 * now. 504 */ 505 if (attrs) { 506 nvlist_free(attrs); 507 break; 508 } 509 } 510 511 if (slices) { 512 dm_free_descriptors(slices); 513 } 514 515 /* 516 * The user is trying to label the disk. We have to do special 517 * checking here to ensure they are not trying to modify a slice 518 * that is in use in an incompatible way. 519 */ 520 if (check_label && bm_inuse) { 521 /* 522 * !0 indicates that we found a 523 * problem. In this case, we have overloaded 524 * the use of checkpartitions to work for 525 * in use devices. bm_inuse is representative 526 * of the slice that is in use, not that 527 * is mounted as is in the case of the normal 528 * use of checkpartitions. 529 * 530 * The call to checkpartitions will return !0 if 531 * we are trying to shrink a device that we have found 532 * to be in use above. 533 */ 534 return (checkpartitions(bm_inuse)); 535 } 536 537 return (found); 538 } 539 /* 540 * This routine checks to see if there are mounted partitions overlapping 541 * a given portion of a disk. If the start parameter is < 0, it means 542 * that the entire disk should be checked. 543 */ 544 int 545 checkmount(diskaddr_t start, diskaddr_t end) 546 { 547 FILE *fp; 548 int found = 0; 549 struct dk_map32 *map; 550 int part; 551 struct mnttab mnt_record; 552 struct mnttab *mp = &mnt_record; 553 554 /* 555 * If we are only checking part of the disk, the disk must 556 * have a partition map to check against. If it doesn't, 557 * we hope for the best. 558 */ 559 if (cur_parts == NULL) 560 return (0); 561 562 /* 563 * Lock out interrupts because of the mntent protocol. 564 */ 565 enter_critical(); 566 /* 567 * Open the mount table. 568 */ 569 fp = fopen(MNTTAB, "r"); 570 if (fp == NULL) { 571 err_print("Unable to open mount table.\n"); 572 fullabort(); 573 } 574 /* 575 * Loop through the mount table until we run out of entries. 576 */ 577 while ((getmntent(fp, mp)) != -1) { 578 579 if ((part = getpartition(mp->mnt_special)) == -1) 580 continue; 581 582 /* 583 * It's a mount on the disk we're checking. If we are 584 * checking whole disk, then we found trouble. We can 585 * quit searching. 586 */ 587 if (start == UINT_MAX64) { 588 found = -1; 589 break; 590 } 591 592 /* 593 * If the partition overlaps the zone we're checking, 594 * then we found trouble. We can quit searching. 595 */ 596 map = &cur_parts->pinfo_map[part]; 597 if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || 598 (end < (int)(map->dkl_cylno * spc()))) { 599 continue; 600 } 601 found = -1; 602 break; 603 } 604 /* 605 * Close down the mount table. 606 */ 607 (void) fclose(fp); 608 exit_critical(); 609 610 /* 611 * If we found trouble and we're running from a command file, 612 * quit before doing something we really regret. 613 */ 614 615 if (found && option_f) { 616 err_print("Operation on mounted disks must be interactive.\n"); 617 cmdabort(SIGINT); 618 } 619 /* 620 * Return the result. 621 */ 622 return (found); 623 } 624 625 int 626 check_label_with_swap(void) 627 { 628 int i; 629 struct swaptable *st; 630 struct swapent *swapent; 631 int part; 632 int bm_swap = 0; 633 634 /* 635 * If we are only checking part of the disk, the disk must 636 * have a partition map to check against. If it doesn't, 637 * we hope for the best. 638 */ 639 if (cur_parts == NULL) 640 return (0); /* Will be checked later */ 641 642 /* 643 * Check for swap entries 644 */ 645 st = getswapentries(); 646 /* 647 * if there are no swap entries return. 648 */ 649 if (st == NULL) 650 return (0); 651 swapent = st->swt_ent; 652 for (i = 0; i < st->swt_n; i++, swapent++) 653 if ((part = getpartition(swapent->ste_path)) != -1) 654 bm_swap |= (1 << part); 655 freeswapentries(st); 656 657 return (checkpartitions(bm_swap)); 658 } 659 660 /* 661 * Check the new label with the existing label on the disk, 662 * to make sure that any mounted partitions are not being 663 * affected by writing the new label. 664 */ 665 int 666 check_label_with_mount(void) 667 { 668 FILE *fp; 669 int part; 670 struct mnttab mnt_record; 671 struct mnttab *mp = &mnt_record; 672 int bm_mounted = 0; 673 674 675 /* 676 * If we are only checking part of the disk, the disk must 677 * have a partition map to check against. If it doesn't, 678 * we hope for the best. 679 */ 680 if (cur_parts == NULL) 681 return (0); /* Will be checked later */ 682 683 /* 684 * Lock out interrupts because of the mntent protocol. 685 */ 686 enter_critical(); 687 /* 688 * Open the mount table. 689 */ 690 fp = fopen(MNTTAB, "r"); 691 if (fp == NULL) { 692 err_print("Unable to open mount table.\n"); 693 fullabort(); 694 } 695 /* 696 * Loop through the mount table until we run out of entries. 697 */ 698 while ((getmntent(fp, mp)) != -1) { 699 if ((part = getpartition(mp->mnt_special)) != -1) 700 bm_mounted |= (1 << part); 701 } 702 /* 703 * Close down the mount table. 704 */ 705 (void) fclose(fp); 706 exit_critical(); 707 708 return (checkpartitions(bm_mounted)); 709 710 } 711 712 /* 713 * This Routine checks if any partitions specified 714 * are affected by writing the new label 715 */ 716 static int 717 checkpartitions(int bm_mounted) 718 { 719 struct dk_map32 *n; 720 struct dk_map *o; 721 struct dk_allmap old_map; 722 int i, found = 0; 723 struct partition64 o_efi; 724 725 /* 726 * Now we need to check that the current partition list and the 727 * previous partition list (which there must be if we actually 728 * have partitions mounted) overlap in any way on the mounted 729 * partitions 730 */ 731 732 /* 733 * Check if the user wants to online-label an 734 * existing EFI label. 735 */ 736 if (cur_label == L_TYPE_EFI) { 737 for (i = 0; i < EFI_NUMPAR; i++) { 738 if (bm_mounted & (1 << i)) { 739 o_efi.p_partno = i; 740 if (ioctl(cur_file, DKIOCPARTITION, &o_efi) 741 == -1) { 742 err_print("Unable to get information " 743 "for EFI partition %d.\n", i); 744 return (-1); 745 } 746 747 /* 748 * Partition can grow or remain same. 749 */ 750 if (o_efi.p_start == cur_parts->etoc-> 751 efi_parts[i].p_start && o_efi.p_size 752 <= cur_parts->etoc->efi_parts[i].p_size) { 753 continue; 754 } 755 756 found = -1; 757 } 758 if (found) 759 break; 760 } 761 762 } else { 763 764 /* 765 * Get the "real" (on-disk) version of the partition table 766 */ 767 if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { 768 err_print("Unable to get current partition map.\n"); 769 return (-1); 770 } 771 for (i = 0; i < NDKMAP; i++) { 772 if (bm_mounted & (1 << i)) { 773 /* 774 * This partition is mounted 775 */ 776 o = &old_map.dka_map[i]; 777 n = &cur_parts->pinfo_map[i]; 778 #ifdef DEBUG 779 fmt_print( 780 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); 781 #endif 782 /* 783 * If partition is identical, we're fine. 784 * If the partition grows, we're also fine, 785 * because the routines in partition.c check 786 * for overflow. It will (ultimately) be up 787 * to the routines in partition.c to warn 788 * about creation of overlapping partitions. 789 */ 790 if (o->dkl_cylno == n->dkl_cylno && 791 o->dkl_nblk <= n->dkl_nblk) { 792 #ifdef DEBUG 793 if (o->dkl_nblk < n->dkl_nblk) { 794 fmt_print( 795 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); 796 } 797 fmt_print("\n"); 798 #endif 799 continue; 800 } 801 #ifdef DEBUG 802 fmt_print("- changes; old (%d,%d)->new " 803 "(%d,%d)\n", o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, n->dkl_nblk); 804 #endif 805 found = -1; 806 } 807 if (found) 808 break; 809 } 810 } 811 812 /* 813 * If we found trouble and we're running from a command file, 814 * quit before doing something we really regret. 815 */ 816 817 if (found && option_f) { 818 err_print("Operation on mounted disks or \ 819 disks currently being used for swapping must be interactive.\n"); 820 cmdabort(SIGINT); 821 } 822 /* 823 * Return the result. 824 */ 825 return (found); 826 } 827