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