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