1 /* 2 * Copyright (c)2004 The DragonFly Project. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * Neither the name of the DragonFly Project nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 31 * OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * diskutil.c 36 * Disk utility functions for installer. 37 * $Id: diskutil.c,v 1.44 2005/02/07 06:41:42 cpressey Exp $ 38 */ 39 40 #include <stdarg.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include "libaura/mem.h" 46 #include "libaura/fspred.h" 47 #include "libaura/popen.h" 48 49 #include "libdfui/dfui.h" 50 #include "libdfui/dump.h" 51 52 #define NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS 53 #include "diskutil.h" 54 #undef NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS 55 56 #include "commands.h" 57 #include "functions.h" 58 #include "sysids.h" 59 #include "uiutil.h" 60 61 static int disk_description_is_better(const char *, const char *); 62 63 /** STORAGE DESCRIPTORS **/ 64 65 struct storage * 66 storage_new(void) 67 { 68 struct storage *s; 69 70 AURA_MALLOC(s, storage); 71 72 s->disk_head = NULL; 73 s->disk_tail = NULL; 74 s->selected_disk = NULL; 75 s->selected_slice = NULL; 76 s->ram = -1; 77 78 return(s); 79 } 80 81 int 82 storage_get_mfs_status(const char *mountpoint, struct storage *s) 83 { 84 struct subpartition *sp; 85 sp = NULL; 86 for (sp = slice_subpartition_first(s->selected_slice); 87 sp != NULL; sp = subpartition_next(sp)) { 88 if(strcmp(subpartition_get_mountpoint(sp), mountpoint) == 0) { 89 if(subpartition_is_mfsbacked(sp) == 1) { 90 return 1; 91 } else { 92 return 0; 93 } 94 } 95 } 96 return 0; 97 } 98 99 void 100 storage_free(struct storage *s) 101 { 102 disks_free(s); 103 AURA_FREE(s, storage); 104 } 105 106 void 107 storage_set_memsize(struct storage *s, unsigned long memsize) 108 { 109 s->ram = memsize; 110 } 111 112 unsigned long 113 storage_get_memsize(const struct storage *s) 114 { 115 return(s->ram); 116 } 117 118 struct disk * 119 storage_disk_first(const struct storage *s) 120 { 121 return(s->disk_head); 122 } 123 124 void 125 storage_set_selected_disk(struct storage *s, struct disk *d) 126 { 127 s->selected_disk = d; 128 } 129 130 struct disk * 131 storage_get_selected_disk(const struct storage *s) 132 { 133 return(s->selected_disk); 134 } 135 136 void 137 storage_set_selected_slice(struct storage *s, struct slice *sl) 138 { 139 s->selected_slice = sl; 140 } 141 142 struct slice * 143 storage_get_selected_slice(const struct storage *s) 144 { 145 return(s->selected_slice); 146 } 147 148 /* 149 * Create a new disk description structure. 150 */ 151 struct disk * 152 disk_new(struct storage *s, const char *dev_name) 153 { 154 struct disk *d; 155 156 if (disk_find(s, dev_name) != NULL) { 157 /* Already discovered */ 158 return(NULL); 159 } 160 161 AURA_MALLOC(d, disk); 162 163 d->device = aura_strdup(dev_name); 164 d->desc = NULL; 165 d->serno = NULL; 166 d->we_formatted = 0; 167 d->capacity = 0; 168 169 d->cylinders = -1; /* -1 indicates "we don't know" */ 170 d->heads = -1; 171 d->sectors = -1; 172 173 d->slice_head = NULL; 174 d->slice_tail = NULL; 175 176 d->next = NULL; 177 if (s->disk_head == NULL) 178 s->disk_head = d; 179 else 180 s->disk_tail->next = d; 181 182 d->prev = s->disk_tail; 183 s->disk_tail = d; 184 185 return(d); 186 } 187 188 static int 189 disk_description_is_better(const char *existing, const char *new_desc __unused) 190 { 191 if (existing == NULL) 192 return(1); 193 return(0); 194 } 195 196 const char * 197 disk_get_desc(const struct disk *d) 198 { 199 return(d->desc); 200 } 201 202 void 203 disk_set_desc(struct disk *d, const char *desc) 204 { 205 char *c; 206 207 if (!disk_description_is_better(d->desc, desc)) 208 return; 209 if (d->desc != NULL) 210 free(d->desc); 211 d->desc = aura_strdup(desc); 212 213 /* 214 * Get the disk's total capacity. 215 * XXX we should do this with C/H/S ? 216 */ 217 c = d->desc; 218 while (*c != ':' && *c != '\0') 219 c++; 220 if (*c == '\0') 221 d->capacity = 0; 222 else 223 d->capacity = atoi(c + 1); 224 } 225 226 /* 227 * Returns the name of the device node used to represent the disk. 228 * Note that the storage used for the returned string is static, 229 * and the string is overwritten each time this function is called. 230 */ 231 const char * 232 disk_get_device_name(const struct disk *d) 233 { 234 static char tmp_dev_name[256]; 235 236 snprintf(tmp_dev_name, 256, "%s", d->device); 237 return(tmp_dev_name); 238 } 239 240 const char * 241 disk_get_serno(const struct disk *d) 242 { 243 return(d->serno); 244 } 245 246 void 247 disk_set_serno(struct disk *d, const char *serno) 248 { 249 d->serno = aura_strdup(serno); 250 } 251 252 int 253 disk_get_number(const struct disk *d) 254 { 255 return(d->number); 256 } 257 258 void 259 disk_set_number(struct disk *d, const int number) 260 { 261 d->number = number; 262 } 263 264 /* 265 * Find the first disk description structure in the given 266 * storage description which matches the given device name 267 * prefix. Note that this means that if a storage 268 * description s contains disks named "ad0" and "ad1", 269 * disk_find(s, "ad0s1c") will return a pointer to the disk 270 * structure for "ad0". 271 */ 272 struct disk * 273 disk_find(const struct storage *s, const char *device) 274 { 275 struct disk *d = s->disk_head; 276 277 while (d != NULL) { 278 if (strncmp(device, d->device, strlen(d->device)) == 0) 279 return(d); 280 d = d->next; 281 } 282 283 return(NULL); 284 } 285 286 struct disk * 287 disk_next(const struct disk *d) 288 { 289 return(d->next); 290 } 291 292 struct slice * 293 disk_slice_first(const struct disk *d) 294 { 295 return(d->slice_head); 296 } 297 298 void 299 disk_set_formatted(struct disk *d, int formatted) 300 { 301 d->we_formatted = formatted; 302 } 303 304 int 305 disk_get_formatted(const struct disk *d) 306 { 307 return(d->we_formatted); 308 } 309 310 void 311 disk_set_geometry(struct disk *d, int cyl, int hd, int sec) 312 { 313 d->cylinders = cyl; 314 d->heads = hd; 315 d->sectors = sec; 316 } 317 318 void 319 disk_get_geometry(const struct disk *d, int *cyl, int *hd, int *sec) 320 { 321 *cyl = d->cylinders; 322 *hd = d->heads; 323 *sec = d->sectors; 324 } 325 326 /* 327 * Free the memory allocated to hold the set of disk descriptions. 328 */ 329 void 330 disks_free(struct storage *s) 331 { 332 struct disk *d = s->disk_head, *next; 333 334 while (d != NULL) { 335 next = d->next; 336 slices_free(d->slice_head); 337 free(d->desc); 338 free(d->device); 339 AURA_FREE(d, disk); 340 d = next; 341 } 342 343 s->disk_head = NULL; 344 s->disk_tail = NULL; 345 } 346 347 /* 348 * Create a new slice description and add it to a disk description. 349 */ 350 struct slice * 351 slice_new(struct disk *d, int number, int type, int flags, 352 unsigned long start, unsigned long size) 353 { 354 struct slice *s; 355 const char *sysid_desc = NULL; 356 char unknown[256]; 357 int i; 358 359 dfui_debug("** adding slice %d (start %ld, size %ld, sysid %d) " 360 "to disk %s\n", number, start, size, type, d->device); 361 362 AURA_MALLOC(s, slice); 363 364 s->parent = d; 365 366 s->subpartition_head = NULL; 367 s->subpartition_tail = NULL; 368 s->number = number; 369 370 s->type = type; 371 s->flags = flags; 372 s->start = start; 373 s->size = size; 374 375 for (i = 0; ; i++) { 376 if (part_types[i].type == type) { 377 sysid_desc = part_types[i].name; 378 break; 379 } 380 if (part_types[i].type == 255) 381 break; 382 } 383 if (sysid_desc == NULL) { 384 snprintf(unknown, 256, "??? Unknown, sysid = %d", type); 385 sysid_desc = unknown; 386 } 387 388 asprintf(&s->desc, "%ldM - %ldM: %s", 389 start / 2048, (start + size) / 2048, sysid_desc); 390 s->capacity = size / 2048; 391 392 s->next = NULL; 393 if (d->slice_head == NULL) 394 d->slice_head = s; 395 else 396 d->slice_tail->next = s; 397 398 s->prev = d->slice_tail; 399 d->slice_tail = s; 400 401 return(s); 402 } 403 404 /* 405 * Find a slice description on a given disk description given the 406 * slice number. 407 */ 408 struct slice * 409 slice_find(const struct disk *d, int number) 410 { 411 struct slice *s = d->slice_head; 412 413 while (s != NULL) { 414 if (s->number == number) 415 return(s); 416 s = s->next; 417 } 418 419 return(NULL); 420 } 421 422 struct slice * 423 slice_next(const struct slice *s) 424 { 425 return(s->next); 426 } 427 428 /* 429 * Returns the name of the device node used to represent the slice. 430 * Note that the storage used for the returned string is static, 431 * and the string is overwritten each time this function is called. 432 */ 433 const char * 434 slice_get_device_name(const struct slice *s) 435 { 436 static char tmp_dev_name[256]; 437 438 snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number); 439 return(tmp_dev_name); 440 } 441 442 int 443 slice_get_number(const struct slice *s) 444 { 445 return(s->number); 446 } 447 448 const char * 449 slice_get_desc(const struct slice *s) 450 { 451 return(s->desc); 452 } 453 454 unsigned long 455 slice_get_capacity(const struct slice *s) 456 { 457 return(s->capacity); 458 } 459 460 unsigned long 461 slice_get_start(const struct slice *s) 462 { 463 return(s->start); 464 } 465 466 unsigned long 467 slice_get_size(const struct slice *s) 468 { 469 return(s->size); 470 } 471 472 int 473 slice_get_type(const struct slice *s) 474 { 475 return(s->type); 476 } 477 478 int 479 slice_get_flags(const struct slice *s) 480 { 481 return(s->flags); 482 } 483 484 struct subpartition * 485 slice_subpartition_first(const struct slice *s) 486 { 487 return(s->subpartition_head); 488 } 489 490 /* 491 * Free all memory for a list of slice descriptions. 492 */ 493 void 494 slices_free(struct slice *head) 495 { 496 struct slice *next; 497 498 while (head != NULL) { 499 next = head->next; 500 subpartitions_free(head); 501 free(head->desc); 502 AURA_FREE(head, slice); 503 head = next; 504 } 505 } 506 507 struct subpartition * 508 subpartition_new_hammer(struct slice *s, const char *mountpoint, long capacity) 509 { 510 struct subpartition *sp; 511 512 AURA_MALLOC(sp, subpartition); 513 514 sp->parent = s; 515 516 struct subpartition *last = s->subpartition_tail; 517 if (last == NULL) { 518 sp->letter = 'a'; 519 } else if (last->letter == 'b') { 520 sp->letter = 'd'; 521 } else { 522 sp->letter = (char)(last->letter + 1); 523 } 524 525 sp->mountpoint = aura_strdup(mountpoint); 526 sp->capacity = capacity; 527 sp->type = FS_HAMMER; 528 529 /* 530 * We need this here, because a UFS /boot needs valid values 531 */ 532 if (sp->capacity < 1024) 533 sp->fsize = 1024; 534 else 535 sp->fsize = 2048; 536 537 if (sp->capacity < 1024) 538 sp->bsize = 8192; 539 else 540 sp->bsize = 16384; 541 542 sp->is_swap = 0; 543 sp->pfs = 0; 544 if (strcasecmp(mountpoint, "swap") == 0) 545 sp->is_swap = 1; 546 if (strcmp(mountpoint, "/") != 0 && strcmp(mountpoint, "/boot") != 0 && 547 strcmp(mountpoint, "swap") != 0) 548 sp->pfs = 1; 549 550 sp->next = NULL; 551 if (s->subpartition_head == NULL) 552 s->subpartition_head = sp; 553 else 554 s->subpartition_tail->next = sp; 555 556 sp->prev = s->subpartition_tail; 557 s->subpartition_tail = sp; 558 559 return(sp); 560 } 561 562 /* 563 * NOTE: arguments to this function are not checked for sanity. 564 * 565 * fsize and/or bsize may both be -1, indicating 566 * "choose a reasonable default." 567 */ 568 struct subpartition * 569 subpartition_new(struct slice *s, const char *mountpoint, long capacity, 570 int softupdates, long fsize, long bsize, int mfsbacked) 571 { 572 struct subpartition *sp, *sptmp; 573 int letter='d'; 574 575 AURA_MALLOC(sp, subpartition); 576 577 sp->parent = s; 578 579 sp->mountpoint = aura_strdup(mountpoint); 580 sp->capacity = capacity; 581 sp->type = FS_UFS; 582 583 if (fsize == -1) { 584 if (sp->capacity < 1024) 585 sp->fsize = 1024; 586 else 587 sp->fsize = 2048; 588 } else { 589 sp->fsize = fsize; 590 } 591 592 if (bsize == -1) { 593 if (sp->capacity < 1024) 594 sp->bsize = 8192; 595 else 596 sp->bsize = 16384; 597 } else { 598 sp->bsize = bsize; 599 } 600 601 if (softupdates == -1) { 602 if (strcmp(mountpoint, "/") == 0) 603 sp->softupdates = 0; 604 else 605 sp->softupdates = 1; 606 } else { 607 sp->softupdates = softupdates; 608 } 609 610 sp->mfsbacked = mfsbacked; 611 612 sp->is_swap = 0; 613 if (strcasecmp(mountpoint, "swap") == 0) 614 sp->is_swap = 1; 615 616 if (s->subpartition_head == NULL) { 617 s->subpartition_head = sp; 618 s->subpartition_tail = sp; 619 } else { 620 for (sptmp = s->subpartition_head; sptmp != NULL; 621 sptmp = sptmp->next) { 622 if (strcmp(sptmp->mountpoint, sp->mountpoint) > 0) 623 break; 624 } 625 if (sptmp != NULL) { 626 if (s->subpartition_head == sptmp) 627 s->subpartition_head = sp; 628 else 629 sptmp->prev->next = sp; 630 sp->next = sptmp; 631 sp->prev = sptmp->prev; 632 sptmp->prev = sp; 633 } else { 634 sp->prev = s->subpartition_tail; 635 s->subpartition_tail->next = sp; 636 s->subpartition_tail = sp; 637 } 638 } 639 640 for (sptmp = s->subpartition_head; sptmp != NULL; 641 sptmp = sptmp->next) { 642 if (sptmp->mfsbacked) 643 sptmp->letter = '@'; 644 else if (strcmp(sptmp->mountpoint, "/") == 0 || 645 strcmp(sptmp->mountpoint, "/dummy") == 0) 646 sptmp->letter = 'a'; 647 else if (strcasecmp(sptmp->mountpoint, "swap") == 0) 648 sptmp->letter = 'b'; 649 else 650 sptmp->letter = letter++; 651 } 652 653 return(sp); 654 } 655 656 /* 657 * Find the subpartition description in the given storage 658 * description whose mountpoint matches the given string exactly. 659 */ 660 struct subpartition * 661 subpartition_find(const struct slice *s, const char *fmt, ...) 662 { 663 struct subpartition *sp = s->subpartition_head; 664 char *mountpoint; 665 va_list args; 666 667 va_start(args, fmt); 668 vasprintf(&mountpoint, fmt, args); 669 va_end(args); 670 671 while (sp != NULL) { 672 if (strcmp(mountpoint, sp->mountpoint) == 0) { 673 free(mountpoint); 674 return(sp); 675 } 676 sp = sp->next; 677 } 678 679 free(mountpoint); 680 return(NULL); 681 } 682 683 /* 684 * Find the subpartition description in the given storage 685 * description where the given filename would presumably 686 * reside. This is the subpartition whose mountpoint is 687 * the longest match for the given filename. 688 */ 689 struct subpartition * 690 subpartition_of(const struct slice *s, const char *fmt, ...) 691 { 692 struct subpartition *sp = s->subpartition_head; 693 struct subpartition *csp = NULL; 694 size_t len = 0; 695 char *filename; 696 va_list args; 697 698 va_start(args, fmt); 699 vasprintf(&filename, fmt, args); 700 va_end(args); 701 702 while (sp != NULL) { 703 if (strlen(sp->mountpoint) > len && 704 strlen(sp->mountpoint) <= strlen(filename) && 705 strncmp(filename, sp->mountpoint, strlen(sp->mountpoint)) == 0) { 706 csp = sp; 707 len = strlen(csp->mountpoint); 708 } 709 sp = sp->next; 710 } 711 712 free(filename); 713 return(csp); 714 } 715 716 struct subpartition * 717 subpartition_find_capacity(const struct slice *s, long capacity) 718 { 719 struct subpartition *sp = s->subpartition_head; 720 721 while (sp != NULL) { 722 if (sp->capacity == capacity) 723 return(sp); 724 sp = sp->next; 725 } 726 727 return(NULL); 728 } 729 730 struct subpartition * 731 subpartition_next(const struct subpartition *sp) 732 { 733 return(sp->next); 734 } 735 736 int 737 subpartition_get_pfs(const struct subpartition *sp) 738 { 739 return(sp->pfs); 740 } 741 742 /* 743 * Returns the name of the device node used to represent 744 * the subpartition, either by serial number or traditional style. 745 * Note that the storage used for the returned string is static, 746 * and the string is overwritten each time this function is called. 747 */ 748 const char * 749 subpartition_get_device_name(const struct subpartition *sp) 750 { 751 static char tmp_dev_name[256]; 752 753 if (sp->parent->parent->serno != NULL) 754 snprintf(tmp_dev_name, 256, "serno/%s.s%d%c", 755 sp->parent->parent->serno, sp->parent->number, sp->letter); 756 else 757 snprintf(tmp_dev_name, 256, "%ss%d%c", 758 sp->parent->parent->device, sp->parent->number, sp->letter); 759 return(tmp_dev_name); 760 } 761 762 const char * 763 subpartition_get_mountpoint(const struct subpartition *sp) 764 { 765 return(sp->mountpoint); 766 } 767 768 char 769 subpartition_get_letter(const struct subpartition *sp) 770 { 771 return(sp->letter); 772 } 773 774 unsigned long 775 subpartition_get_fsize(const struct subpartition *sp) 776 { 777 return(sp->fsize); 778 } 779 780 unsigned long 781 subpartition_get_bsize(const struct subpartition *sp) 782 { 783 return(sp->bsize); 784 } 785 786 unsigned long 787 subpartition_get_capacity(const struct subpartition *sp) 788 { 789 return(sp->capacity); 790 } 791 792 int 793 subpartition_is_swap(const struct subpartition *sp) 794 { 795 return(sp->is_swap); 796 } 797 798 int 799 subpartition_is_softupdated(const struct subpartition *sp) 800 { 801 return(sp->softupdates); 802 } 803 int 804 subpartition_is_mfsbacked(const struct subpartition *sp) 805 { 806 return(sp->mfsbacked); 807 } 808 809 int 810 subpartition_count(const struct slice *s) 811 { 812 struct subpartition *sp = s->subpartition_head; 813 int count = 0; 814 815 while (sp != NULL) { 816 count++; 817 sp = sp->next; 818 } 819 820 return(count); 821 } 822 823 void 824 subpartitions_free(struct slice *s) 825 { 826 struct subpartition *sp = s->subpartition_head, *next; 827 828 while (sp != NULL) { 829 next = sp->next; 830 free(sp->mountpoint); 831 AURA_FREE(sp, subpartition); 832 sp = next; 833 } 834 835 s->subpartition_head = NULL; 836 s->subpartition_tail = NULL; 837 } 838 839 long 840 measure_activated_swap(const struct i_fn_args *a) 841 { 842 FILE *p; 843 char line[256]; 844 char *word; 845 long swap = 0; 846 847 if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL) 848 return(0); 849 while (fgets(line, 255, p) != NULL) { 850 if ((word = strtok(line, " \t")) == NULL) 851 continue; 852 if (strcmp(word, "Device") == 0) 853 continue; 854 if ((word = strtok(NULL, " \t")) == NULL) 855 continue; 856 swap += atol(word); 857 } 858 aura_pclose(p); 859 860 return(swap / 1024); 861 } 862 863 long 864 measure_activated_swap_from_slice(const struct i_fn_args *a, 865 const struct disk *d, const struct slice *s) 866 { 867 FILE *p; 868 char *dev, *word; 869 char line[256]; 870 long swap = 0; 871 872 if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL) 873 return(0); 874 875 asprintf(&dev, "/dev/%ss%d", d->device, s->number); 876 877 while (fgets(line, 255, p) != NULL) { 878 if ((word = strtok(line, " \t")) == NULL) 879 continue; 880 if (strcmp(word, "Device") == 0) 881 continue; 882 if (strstr(word, dev) != word) 883 continue; 884 if ((word = strtok(NULL, " \t")) == NULL) 885 continue; 886 swap += atol(word); 887 } 888 aura_pclose(p); 889 free(dev); 890 891 return(swap / 1024); 892 } 893 894 long 895 measure_activated_swap_from_disk(const struct i_fn_args *a, 896 const struct disk *d) 897 { 898 struct slice *s; 899 long swap = 0; 900 901 for (s = d->slice_head; s != NULL; s = s->next) 902 swap += measure_activated_swap_from_slice(a, d, s); 903 904 return(swap); 905 } 906