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