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 2006 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 err_print("Error occurred with device in use checking: %s\n", 343 strerror(error)); 344 return (found); 345 } 346 if (slices == NULL) 347 return (found); 348 349 for (i = 0; slices[i] != NULL; i++) { 350 /* 351 * If we are checking the whole disk 352 * then any and all in use data is 353 * relevant. 354 */ 355 if (start == UINT_MAX64) { 356 name = dm_get_name(slices[i], &error); 357 if (error != 0 || !name) { 358 err_print("Error occurred with device " 359 "in use checking: %s\n", 360 strerror(error)); 361 continue; 362 } 363 if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || 364 error) { 365 if (error != 0) { 366 dm_free_name(name); 367 name = NULL; 368 err_print("Error occurred with device " 369 "in use checking: %s\n", 370 strerror(error)); 371 continue; 372 } 373 dm_free_name(name); 374 name = NULL; 375 /* 376 * If this is a dump device, then it is 377 * a failure. You cannot format a slice 378 * that is a dedicated dump device. 379 */ 380 381 if (strstr(usage, DM_USE_DUMP)) { 382 if (print) { 383 err_print(usage); 384 free(usage); 385 } 386 dm_free_descriptors(slices); 387 return (1); 388 } 389 /* 390 * We really found a device that is in use. 391 * Set 'found' for the return value, and set 392 * 'check' to indicate below that we must 393 * get the partition number to set bm_inuse 394 * in the event we are trying to label this 395 * device. check_label is set when we are 396 * checking modifications for in use slices 397 * on the device. 398 */ 399 found ++; 400 check = 1; 401 if (print) { 402 err_print(usage); 403 free(usage); 404 } 405 } 406 } else { 407 /* 408 * Before getting the in use data, verify that the 409 * current slice is within the range we are checking. 410 */ 411 attrs = dm_get_attributes(slices[i], &error); 412 if (error) { 413 err_print("Error occurred with device in use " 414 "checking: %s\n", strerror(error)); 415 continue; 416 } 417 if (attrs == NULL) { 418 continue; 419 } 420 421 (void) nvlist_lookup_uint64(attrs, DM_START, 422 &slice_start); 423 (void) nvlist_lookup_uint64(attrs, DM_SIZE, 424 &slice_size); 425 if (start >= (slice_start + slice_size) || 426 (end < slice_start)) { 427 nvlist_free(attrs); 428 attrs = NULL; 429 continue; 430 } 431 name = dm_get_name(slices[i], &error); 432 if (error != 0 || !name) { 433 err_print("Error occurred with device " 434 "in use checking: %s\n", 435 strerror(error)); 436 nvlist_free(attrs); 437 attrs = NULL; 438 continue; 439 } 440 if (dm_inuse(name, &usage, 441 DM_WHO_FORMAT, &error) || error) { 442 if (error != 0) { 443 dm_free_name(name); 444 name = NULL; 445 err_print("Error occurred with device " 446 "in use checking: %s\n", 447 strerror(error)); 448 nvlist_free(attrs); 449 attrs = NULL; 450 continue; 451 } 452 dm_free_name(name); 453 name = NULL; 454 /* 455 * If this is a dump device, then it is 456 * a failure. You cannot format a slice 457 * that is a dedicated dump device. 458 */ 459 if (strstr(usage, DM_USE_DUMP)) { 460 if (print) { 461 err_print(usage); 462 free(usage); 463 } 464 dm_free_descriptors(slices); 465 nvlist_free(attrs); 466 return (1); 467 } 468 /* 469 * We really found a device that is in use. 470 * Set 'found' for the return value, and set 471 * 'check' to indicate below that we must 472 * get the partition number to set bm_inuse 473 * in the event we are trying to label this 474 * device. check_label is set when we are 475 * checking modifications for in use slices 476 * on the device. 477 */ 478 found ++; 479 check = 1; 480 if (print) { 481 err_print(usage); 482 free(usage); 483 } 484 } 485 } 486 /* 487 * If check is set it means we found a slice(the current slice) 488 * on this device in use in some way. We potentially want 489 * to check this slice when labeling is 490 * requested. We set bm_inuse with this partition value 491 * for use later if check_label was set when called. 492 */ 493 if (check) { 494 name = dm_get_name(slices[i], &error); 495 if (error != 0 || !name) { 496 err_print("Error occurred with device " 497 "in use checking: %s\n", 498 strerror(error)); 499 nvlist_free(attrs); 500 attrs = NULL; 501 continue; 502 } 503 part = getpartition(name); 504 dm_free_name(name); 505 name = NULL; 506 if (part != -1) { 507 bm_inuse |= 1 << part; 508 } 509 check = 0; 510 } 511 /* 512 * If we have attributes then we have successfully 513 * found the slice we were looking for and we also 514 * know this means we are not searching the whole 515 * disk so break out of the loop 516 * now. 517 */ 518 if (attrs) { 519 nvlist_free(attrs); 520 break; 521 } 522 } 523 524 if (slices) { 525 dm_free_descriptors(slices); 526 } 527 528 /* 529 * The user is trying to label the disk. We have to do special 530 * checking here to ensure they are not trying to modify a slice 531 * that is in use in an incompatible way. 532 */ 533 if (check_label && bm_inuse) { 534 /* 535 * !0 indicates that we found a 536 * problem. In this case, we have overloaded 537 * the use of checkpartitions to work for 538 * in use devices. bm_inuse is representative 539 * of the slice that is in use, not that 540 * is mounted as is in the case of the normal 541 * use of checkpartitions. 542 * 543 * The call to checkpartitions will return !0 if 544 * we are trying to shrink a device that we have found 545 * to be in use above. 546 */ 547 return (checkpartitions(bm_inuse)); 548 } 549 550 return (found); 551 } 552 /* 553 * This routine checks to see if there are mounted partitions overlapping 554 * a given portion of a disk. If the start parameter is < 0, it means 555 * that the entire disk should be checked. 556 */ 557 int 558 checkmount(start, end) 559 diskaddr_t start, end; 560 { 561 FILE *fp; 562 int found = 0; 563 struct dk_map32 *map; 564 int part; 565 struct mnttab mnt_record; 566 struct mnttab *mp = &mnt_record; 567 568 /* 569 * If we are only checking part of the disk, the disk must 570 * have a partition map to check against. If it doesn't, 571 * we hope for the best. 572 */ 573 if (cur_parts == NULL) 574 return (0); 575 576 /* 577 * Lock out interrupts because of the mntent protocol. 578 */ 579 enter_critical(); 580 /* 581 * Open the mount table. 582 */ 583 fp = fopen(MNTTAB, "r"); 584 if (fp == NULL) { 585 err_print("Unable to open mount table.\n"); 586 fullabort(); 587 } 588 /* 589 * Loop through the mount table until we run out of entries. 590 */ 591 while ((getmntent(fp, mp)) != -1) { 592 593 if ((part = getpartition(mp->mnt_special)) == -1) 594 continue; 595 596 /* 597 * It's a mount on the disk we're checking. If we are 598 * checking whole disk, then we found trouble. We can 599 * quit searching. 600 */ 601 if (start == UINT_MAX64) { 602 found = -1; 603 break; 604 } 605 606 /* 607 * If the partition overlaps the zone we're checking, 608 * then we found trouble. We can quit searching. 609 */ 610 map = &cur_parts->pinfo_map[part]; 611 if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || 612 (end < (int)(map->dkl_cylno * spc()))) { 613 continue; 614 } 615 found = -1; 616 break; 617 } 618 /* 619 * Close down the mount table. 620 */ 621 (void) fclose(fp); 622 exit_critical(); 623 624 /* 625 * If we found trouble and we're running from a command file, 626 * quit before doing something we really regret. 627 */ 628 629 if (found && option_f) { 630 err_print("Operation on mounted disks must be interactive.\n"); 631 cmdabort(SIGINT); 632 } 633 /* 634 * Return the result. 635 */ 636 return (found); 637 } 638 639 int 640 check_label_with_swap() 641 { 642 int i; 643 struct swaptable *st; 644 struct swapent *swapent; 645 int part; 646 int bm_swap = 0; 647 648 /* 649 * If we are only checking part of the disk, the disk must 650 * have a partition map to check against. If it doesn't, 651 * we hope for the best. 652 */ 653 if (cur_parts == NULL) 654 return (0); /* Will be checked later */ 655 656 /* 657 * Check for swap entries 658 */ 659 st = getswapentries(); 660 /* 661 * if there are no swap entries return. 662 */ 663 if (st == (struct swaptable *)NULL) 664 return (0); 665 swapent = st->swt_ent; 666 for (i = 0; i < st->swt_n; i++, swapent++) 667 if ((part = getpartition(swapent->ste_path)) != -1) 668 bm_swap |= (1 << part); 669 freeswapentries(st); 670 671 return (checkpartitions(bm_swap)); 672 } 673 674 /* 675 * Check the new label with the existing label on the disk, 676 * to make sure that any mounted partitions are not being 677 * affected by writing the new label. 678 */ 679 int 680 check_label_with_mount() 681 { 682 FILE *fp; 683 int part; 684 struct mnttab mnt_record; 685 struct mnttab *mp = &mnt_record; 686 int bm_mounted = 0; 687 688 689 /* 690 * If we are only checking part of the disk, the disk must 691 * have a partition map to check against. If it doesn't, 692 * we hope for the best. 693 */ 694 if (cur_parts == NULL) 695 return (0); /* Will be checked later */ 696 697 /* 698 * Lock out interrupts because of the mntent protocol. 699 */ 700 enter_critical(); 701 /* 702 * Open the mount table. 703 */ 704 fp = fopen(MNTTAB, "r"); 705 if (fp == NULL) { 706 err_print("Unable to open mount table.\n"); 707 fullabort(); 708 } 709 /* 710 * Loop through the mount table until we run out of entries. 711 */ 712 while ((getmntent(fp, mp)) != -1) { 713 if ((part = getpartition(mp->mnt_special)) != -1) 714 bm_mounted |= (1 << part); 715 } 716 /* 717 * Close down the mount table. 718 */ 719 (void) fclose(fp); 720 exit_critical(); 721 722 return (checkpartitions(bm_mounted)); 723 724 } 725 726 /* 727 * This Routine checks if any partitions specified 728 * are affected by writing the new label 729 */ 730 static int 731 checkpartitions(int bm_mounted) 732 { 733 struct dk_map32 *n; 734 struct dk_map *o; 735 struct dk_allmap old_map; 736 int i, found = 0; 737 738 /* 739 * Now we need to check that the current partition list and the 740 * previous partition list (which there must be if we actually 741 * have partitions mounted) overlap in any way on the mounted 742 * partitions 743 */ 744 745 /* 746 * Get the "real" (on-disk) version of the partition table 747 */ 748 if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { 749 err_print("Unable to get current partition map.\n"); 750 return (-1); 751 } 752 for (i = 0; i < NDKMAP; i++) { 753 if (bm_mounted & (1 << i)) { 754 /* 755 * This partition is mounted 756 */ 757 o = &old_map.dka_map[i]; 758 n = &cur_parts->pinfo_map[i]; 759 #ifdef DEBUG 760 fmt_print( 761 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); 762 #endif 763 /* 764 * If partition is identical, we're fine. 765 * If the partition grows, we're also fine, because 766 * the routines in partition.c check for overflow. 767 * It will (ultimately) be up to the routines in 768 * partition.c to warn about creation of overlapping 769 * partitions 770 */ 771 if (o->dkl_cylno == n->dkl_cylno && 772 o->dkl_nblk <= n->dkl_nblk) { 773 #ifdef DEBUG 774 if (o->dkl_nblk < n->dkl_nblk) { 775 fmt_print( 776 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); 777 } 778 fmt_print("\n"); 779 #endif 780 continue; 781 } 782 #ifdef DEBUG 783 fmt_print("- changes; old (%d,%d)->new (%d,%d)\n", 784 o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, 785 n->dkl_nblk); 786 #endif 787 found = -1; 788 } 789 if (found) 790 break; 791 } 792 793 /* 794 * If we found trouble and we're running from a command file, 795 * quit before doing something we really regret. 796 */ 797 798 if (found && option_f) { 799 err_print("Operation on mounted disks or \ 800 disks currently being used for swapping must be interactive.\n"); 801 cmdabort(SIGINT); 802 } 803 /* 804 * Return the result. 805 */ 806 return (found); 807 } 808