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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains miscellaneous device validation routines. 31 */ 32 33 #include "global.h" 34 #include <sys/mnttab.h> 35 #include <sys/mntent.h> 36 #include <sys/autoconf.h> 37 38 #include <signal.h> 39 #include <malloc.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <libgen.h> 45 #include <sys/ioctl.h> 46 #include <sys/fcntl.h> 47 #include <sys/stat.h> 48 #include <sys/swap.h> 49 #include <sys/sysmacros.h> 50 #include <sys/mkdev.h> 51 #include <sys/modctl.h> 52 #include <ctype.h> 53 #include <libdiskmgt.h> 54 #include <libnvpair.h> 55 #include "misc.h" 56 #include "checkdev.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 *name = (char)0; 331 } 332 333 334 /* 335 * For format, we get basic 'in use' details from libdiskmgt. After 336 * that we must do the appropriate checking to see if the 'in use' 337 * details require a bit of additional work. 338 */ 339 340 dm_get_slices(cur_disk_path, &slices, &error); 341 if (error) { 342 /* 343 * If ENODEV, it actually means the device is not in use. 344 * We will return 0 without displaying error. 345 */ 346 if (error != ENODEV) { 347 err_print("Error occurred with device in use" 348 "checking: %s\n", strerror(error)); 349 return (found); 350 } 351 } 352 if (slices == NULL) 353 return (found); 354 355 for (i = 0; slices[i] != NULL; i++) { 356 /* 357 * If we are checking the whole disk 358 * then any and all in use data is 359 * relevant. 360 */ 361 if (start == UINT_MAX64) { 362 name = dm_get_name(slices[i], &error); 363 if (error != 0 || !name) { 364 err_print("Error occurred with device " 365 "in use checking: %s\n", 366 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 device " 375 "in use checking: %s\n", 376 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", 441 strerror(error)); 442 nvlist_free(attrs); 443 attrs = NULL; 444 continue; 445 } 446 if (dm_inuse(name, &usage, 447 DM_WHO_FORMAT, &error) || error) { 448 if (error != 0) { 449 dm_free_name(name); 450 name = NULL; 451 err_print("Error occurred with device " 452 "in use checking: %s\n", 453 strerror(error)); 454 nvlist_free(attrs); 455 attrs = NULL; 456 continue; 457 } 458 dm_free_name(name); 459 name = NULL; 460 /* 461 * If this is a dump device, then it is 462 * a failure. You cannot format a slice 463 * that is a dedicated dump device. 464 */ 465 if (strstr(usage, DM_USE_DUMP)) { 466 if (print) { 467 err_print(usage); 468 free(usage); 469 } 470 dm_free_descriptors(slices); 471 nvlist_free(attrs); 472 return (1); 473 } 474 /* 475 * We really found a device that is in use. 476 * Set 'found' for the return value, and set 477 * 'check' to indicate below that we must 478 * get the partition number to set bm_inuse 479 * in the event we are trying to label this 480 * device. check_label is set when we are 481 * checking modifications for in use slices 482 * on the device. 483 */ 484 found ++; 485 check = 1; 486 if (print) { 487 err_print(usage); 488 free(usage); 489 } 490 } 491 } 492 /* 493 * If check is set it means we found a slice(the current slice) 494 * on this device in use in some way. We potentially want 495 * to check this slice when labeling is 496 * requested. We set bm_inuse with this partition value 497 * for use later if check_label was set when called. 498 */ 499 if (check) { 500 name = dm_get_name(slices[i], &error); 501 if (error != 0 || !name) { 502 err_print("Error occurred with device " 503 "in use checking: %s\n", 504 strerror(error)); 505 nvlist_free(attrs); 506 attrs = NULL; 507 continue; 508 } 509 part = getpartition(name); 510 dm_free_name(name); 511 name = NULL; 512 if (part != -1) { 513 bm_inuse |= 1 << part; 514 } 515 check = 0; 516 } 517 /* 518 * If we have attributes then we have successfully 519 * found the slice we were looking for and we also 520 * know this means we are not searching the whole 521 * disk so break out of the loop 522 * now. 523 */ 524 if (attrs) { 525 nvlist_free(attrs); 526 break; 527 } 528 } 529 530 if (slices) { 531 dm_free_descriptors(slices); 532 } 533 534 /* 535 * The user is trying to label the disk. We have to do special 536 * checking here to ensure they are not trying to modify a slice 537 * that is in use in an incompatible way. 538 */ 539 if (check_label && bm_inuse) { 540 /* 541 * !0 indicates that we found a 542 * problem. In this case, we have overloaded 543 * the use of checkpartitions to work for 544 * in use devices. bm_inuse is representative 545 * of the slice that is in use, not that 546 * is mounted as is in the case of the normal 547 * use of checkpartitions. 548 * 549 * The call to checkpartitions will return !0 if 550 * we are trying to shrink a device that we have found 551 * to be in use above. 552 */ 553 return (checkpartitions(bm_inuse)); 554 } 555 556 return (found); 557 } 558 /* 559 * This routine checks to see if there are mounted partitions overlapping 560 * a given portion of a disk. If the start parameter is < 0, it means 561 * that the entire disk should be checked. 562 */ 563 int 564 checkmount(start, end) 565 diskaddr_t start, end; 566 { 567 FILE *fp; 568 int found = 0; 569 struct dk_map32 *map; 570 int part; 571 struct mnttab mnt_record; 572 struct mnttab *mp = &mnt_record; 573 574 /* 575 * If we are only checking part of the disk, the disk must 576 * have a partition map to check against. If it doesn't, 577 * we hope for the best. 578 */ 579 if (cur_parts == NULL) 580 return (0); 581 582 /* 583 * Lock out interrupts because of the mntent protocol. 584 */ 585 enter_critical(); 586 /* 587 * Open the mount table. 588 */ 589 fp = fopen(MNTTAB, "r"); 590 if (fp == NULL) { 591 err_print("Unable to open mount table.\n"); 592 fullabort(); 593 } 594 /* 595 * Loop through the mount table until we run out of entries. 596 */ 597 while ((getmntent(fp, mp)) != -1) { 598 599 if ((part = getpartition(mp->mnt_special)) == -1) 600 continue; 601 602 /* 603 * It's a mount on the disk we're checking. If we are 604 * checking whole disk, then we found trouble. We can 605 * quit searching. 606 */ 607 if (start == UINT_MAX64) { 608 found = -1; 609 break; 610 } 611 612 /* 613 * If the partition overlaps the zone we're checking, 614 * then we found trouble. We can quit searching. 615 */ 616 map = &cur_parts->pinfo_map[part]; 617 if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || 618 (end < (int)(map->dkl_cylno * spc()))) { 619 continue; 620 } 621 found = -1; 622 break; 623 } 624 /* 625 * Close down the mount table. 626 */ 627 (void) fclose(fp); 628 exit_critical(); 629 630 /* 631 * If we found trouble and we're running from a command file, 632 * quit before doing something we really regret. 633 */ 634 635 if (found && option_f) { 636 err_print("Operation on mounted disks must be interactive.\n"); 637 cmdabort(SIGINT); 638 } 639 /* 640 * Return the result. 641 */ 642 return (found); 643 } 644 645 int 646 check_label_with_swap() 647 { 648 int i; 649 struct swaptable *st; 650 struct swapent *swapent; 651 int part; 652 int bm_swap = 0; 653 654 /* 655 * If we are only checking part of the disk, the disk must 656 * have a partition map to check against. If it doesn't, 657 * we hope for the best. 658 */ 659 if (cur_parts == NULL) 660 return (0); /* Will be checked later */ 661 662 /* 663 * Check for swap entries 664 */ 665 st = getswapentries(); 666 /* 667 * if there are no swap entries return. 668 */ 669 if (st == (struct swaptable *)NULL) 670 return (0); 671 swapent = st->swt_ent; 672 for (i = 0; i < st->swt_n; i++, swapent++) 673 if ((part = getpartition(swapent->ste_path)) != -1) 674 bm_swap |= (1 << part); 675 freeswapentries(st); 676 677 return (checkpartitions(bm_swap)); 678 } 679 680 /* 681 * Check the new label with the existing label on the disk, 682 * to make sure that any mounted partitions are not being 683 * affected by writing the new label. 684 */ 685 int 686 check_label_with_mount() 687 { 688 FILE *fp; 689 int part; 690 struct mnttab mnt_record; 691 struct mnttab *mp = &mnt_record; 692 int bm_mounted = 0; 693 694 695 /* 696 * If we are only checking part of the disk, the disk must 697 * have a partition map to check against. If it doesn't, 698 * we hope for the best. 699 */ 700 if (cur_parts == NULL) 701 return (0); /* Will be checked later */ 702 703 /* 704 * Lock out interrupts because of the mntent protocol. 705 */ 706 enter_critical(); 707 /* 708 * Open the mount table. 709 */ 710 fp = fopen(MNTTAB, "r"); 711 if (fp == NULL) { 712 err_print("Unable to open mount table.\n"); 713 fullabort(); 714 } 715 /* 716 * Loop through the mount table until we run out of entries. 717 */ 718 while ((getmntent(fp, mp)) != -1) { 719 if ((part = getpartition(mp->mnt_special)) != -1) 720 bm_mounted |= (1 << part); 721 } 722 /* 723 * Close down the mount table. 724 */ 725 (void) fclose(fp); 726 exit_critical(); 727 728 return (checkpartitions(bm_mounted)); 729 730 } 731 732 /* 733 * This Routine checks if any partitions specified 734 * are affected by writing the new label 735 */ 736 static int 737 checkpartitions(int bm_mounted) 738 { 739 struct dk_map32 *n; 740 struct dk_map *o; 741 struct dk_allmap old_map; 742 int i, found = 0; 743 744 /* 745 * Now we need to check that the current partition list and the 746 * previous partition list (which there must be if we actually 747 * have partitions mounted) overlap in any way on the mounted 748 * partitions 749 */ 750 751 /* 752 * Get the "real" (on-disk) version of the partition table 753 */ 754 if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { 755 err_print("Unable to get current partition map.\n"); 756 return (-1); 757 } 758 for (i = 0; i < NDKMAP; i++) { 759 if (bm_mounted & (1 << i)) { 760 /* 761 * This partition is mounted 762 */ 763 o = &old_map.dka_map[i]; 764 n = &cur_parts->pinfo_map[i]; 765 #ifdef DEBUG 766 fmt_print( 767 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); 768 #endif 769 /* 770 * If partition is identical, we're fine. 771 * If the partition grows, we're also fine, because 772 * the routines in partition.c check for overflow. 773 * It will (ultimately) be up to the routines in 774 * partition.c to warn about creation of overlapping 775 * partitions 776 */ 777 if (o->dkl_cylno == n->dkl_cylno && 778 o->dkl_nblk <= n->dkl_nblk) { 779 #ifdef DEBUG 780 if (o->dkl_nblk < n->dkl_nblk) { 781 fmt_print( 782 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); 783 } 784 fmt_print("\n"); 785 #endif 786 continue; 787 } 788 #ifdef DEBUG 789 fmt_print("- changes; old (%d,%d)->new (%d,%d)\n", 790 o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, 791 n->dkl_nblk); 792 #endif 793 found = -1; 794 } 795 if (found) 796 break; 797 } 798 799 /* 800 * If we found trouble and we're running from a command file, 801 * quit before doing something we really regret. 802 */ 803 804 if (found && option_f) { 805 err_print("Operation on mounted disks or \ 806 disks currently being used for swapping must be interactive.\n"); 807 cmdabort(SIGINT); 808 } 809 /* 810 * Return the result. 811 */ 812 return (found); 813 } 814