1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2004, 2007 Lukas Ertl 5 * Copyright (c) 1997, 1998, 1999 6 * Nan Yang Computer Services Limited. All rights reserved. 7 * 8 * Parts written by Greg Lehey 9 * 10 * This software is distributed under the so-called ``Berkeley 11 * License'': 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by Nan Yang Computer 24 * Services Limited. 25 * 4. Neither the name of the Company nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * This software is provided ``as is'', and any express or implied 30 * warranties, including, but not limited to, the implied warranties of 31 * merchantability and fitness for a particular purpose are disclaimed. 32 * In no event shall the company or contributors be liable for any 33 * direct, indirect, incidental, special, exemplary, or consequential 34 * damages (including, but not limited to, procurement of substitute 35 * goods or services; loss of use, data, or profits; or business 36 * interruption) however caused and on any theory of liability, whether 37 * in contract, strict liability, or tort (including negligence or 38 * otherwise) arising in any way out of the use of this software, even if 39 * advised of the possibility of such damage. 40 * 41 */ 42 43 /* This file is shared between kernel and userland. */ 44 45 #include <sys/cdefs.h> 46 __FBSDID("$FreeBSD$"); 47 48 #include <sys/param.h> 49 #ifdef _KERNEL 50 #include <sys/malloc.h> 51 #include <sys/systm.h> 52 53 #include <geom/geom.h> 54 #define iswhite(c) (((c) == ' ') || ((c) == '\t')) 55 #else 56 #include <ctype.h> 57 #include <paths.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #define iswhite isspace 62 #define g_free free 63 #endif /* _KERNEL */ 64 65 #include <sys/mutex.h> 66 #include <sys/queue.h> 67 68 #include <geom/vinum/geom_vinum_var.h> 69 #include <geom/vinum/geom_vinum_share.h> 70 71 /* 72 * Take a blank separated list of tokens and turn it into a list of 73 * individual nul-delimited strings. Build a list of pointers at 74 * token, which must have enough space for the tokens. Return the 75 * number of tokens, or -1 on error (typically a missing string 76 * delimiter). 77 */ 78 int 79 gv_tokenize(char *cptr, char *token[], int maxtoken) 80 { 81 int tokennr; /* Index of this token. */ 82 char delim; /* Delimiter for searching for the partner. */ 83 84 for (tokennr = 0; tokennr < maxtoken;) { 85 86 /* Skip leading white space. */ 87 while (iswhite(*cptr)) 88 cptr++; 89 90 /* End of line. */ 91 if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#')) 92 return tokennr; 93 94 delim = *cptr; 95 token[tokennr] = cptr; /* Point to it. */ 96 tokennr++; /* One more. */ 97 98 /* Run off the end? */ 99 if (tokennr == maxtoken) 100 return tokennr; 101 102 /* Quoted? */ 103 if ((delim == '\'') || (delim == '"')) { 104 for (;;) { 105 cptr++; 106 107 /* Found the partner. */ 108 if ((*cptr == delim) && (cptr[-1] != '\\')) { 109 cptr++; 110 111 /* Space after closing quote needed. */ 112 if (!iswhite(*cptr)) 113 return -1; 114 115 /* Delimit. */ 116 *cptr++ = '\0'; 117 118 /* End-of-line? */ 119 } else if ((*cptr == '\0') || (*cptr == '\n')) 120 return -1; 121 } 122 123 /* Not quoted. */ 124 } else { 125 while ((*cptr != '\0') && 126 (!iswhite(*cptr)) && 127 (*cptr != '\n')) 128 cptr++; 129 130 /* Not end-of-line; delimit and move to the next. */ 131 if (*cptr != '\0') 132 *cptr++ = '\0'; 133 } 134 } 135 136 /* Can't get here. */ 137 return maxtoken; 138 } 139 140 141 /* 142 * Take a number with an optional scale factor and convert it to a number of 143 * bytes. 144 * 145 * The scale factors are: 146 * 147 * s sectors (of 512 bytes) 148 * b blocks (of 512 bytes). This unit is deprecated, because it's 149 * confusing, but maintained to avoid confusing Veritas users. 150 * k kilobytes (1024 bytes) 151 * m megabytes (of 1024 * 1024 bytes) 152 * g gigabytes (of 1024 * 1024 * 1024 bytes) 153 * 154 * XXX: need a way to signal error 155 */ 156 off_t 157 gv_sizespec(char *spec) 158 { 159 uint64_t size; 160 char *s; 161 int sign; 162 163 size = 0; 164 sign = 1; 165 if (spec != NULL) { /* we have a parameter */ 166 s = spec; 167 if (*s == '-') { /* negative, */ 168 sign = -1; 169 s++; /* skip */ 170 } 171 172 /* It's numeric. */ 173 if ((*s >= '0') && (*s <= '9')) { 174 175 /* It's numeric. */ 176 while ((*s >= '0') && (*s <= '9')) 177 /* Convert it. */ 178 size = size * 10 + *s++ - '0'; 179 180 switch (*s) { 181 case '\0': 182 return size * sign; 183 184 case 'B': 185 case 'b': 186 case 'S': 187 case 's': 188 return size * sign * 512; 189 190 case 'K': 191 case 'k': 192 return size * sign * 1024; 193 194 case 'M': 195 case 'm': 196 return size * sign * 1024 * 1024; 197 198 case 'G': 199 case 'g': 200 return size * sign * 1024 * 1024 * 1024; 201 } 202 } 203 } 204 205 return (0); 206 } 207 208 const char * 209 gv_drivestate(int state) 210 { 211 switch (state) { 212 case GV_DRIVE_DOWN: 213 return "down"; 214 case GV_DRIVE_UP: 215 return "up"; 216 default: 217 return "??"; 218 } 219 } 220 221 int 222 gv_drivestatei(char *buf) 223 { 224 if (!strcmp(buf, "up")) 225 return (GV_DRIVE_UP); 226 else 227 return (GV_DRIVE_DOWN); 228 } 229 230 /* Translate from a string to a subdisk state. */ 231 int 232 gv_sdstatei(char *buf) 233 { 234 if (!strcmp(buf, "up")) 235 return (GV_SD_UP); 236 else if (!strcmp(buf, "reviving")) 237 return (GV_SD_REVIVING); 238 else if (!strcmp(buf, "initializing")) 239 return (GV_SD_INITIALIZING); 240 else if (!strcmp(buf, "stale")) 241 return (GV_SD_STALE); 242 else 243 return (GV_SD_DOWN); 244 } 245 246 /* Translate from a subdisk state to a string. */ 247 const char * 248 gv_sdstate(int state) 249 { 250 switch (state) { 251 case GV_SD_INITIALIZING: 252 return "initializing"; 253 case GV_SD_STALE: 254 return "stale"; 255 case GV_SD_DOWN: 256 return "down"; 257 case GV_SD_REVIVING: 258 return "reviving"; 259 case GV_SD_UP: 260 return "up"; 261 default: 262 return "??"; 263 } 264 } 265 266 /* Translate from a string to a plex state. */ 267 int 268 gv_plexstatei(char *buf) 269 { 270 if (!strcmp(buf, "up")) 271 return (GV_PLEX_UP); 272 else if (!strcmp(buf, "initializing")) 273 return (GV_PLEX_INITIALIZING); 274 else if (!strcmp(buf, "degraded")) 275 return (GV_PLEX_DEGRADED); 276 else if (!strcmp(buf, "growable")) 277 return (GV_PLEX_GROWABLE); 278 else 279 return (GV_PLEX_DOWN); 280 } 281 282 /* Translate from a plex state to a string. */ 283 const char * 284 gv_plexstate(int state) 285 { 286 switch (state) { 287 case GV_PLEX_DOWN: 288 return "down"; 289 case GV_PLEX_INITIALIZING: 290 return "initializing"; 291 case GV_PLEX_DEGRADED: 292 return "degraded"; 293 case GV_PLEX_GROWABLE: 294 return "growable"; 295 case GV_PLEX_UP: 296 return "up"; 297 default: 298 return "??"; 299 } 300 } 301 302 /* Translate from a string to a plex organization. */ 303 int 304 gv_plexorgi(char *buf) 305 { 306 if (!strcmp(buf, "concat")) 307 return (GV_PLEX_CONCAT); 308 else if (!strcmp(buf, "striped")) 309 return (GV_PLEX_STRIPED); 310 else if (!strcmp(buf, "raid5")) 311 return (GV_PLEX_RAID5); 312 else 313 return (GV_PLEX_DISORG); 314 } 315 316 int 317 gv_volstatei(char *buf) 318 { 319 if (!strcmp(buf, "up")) 320 return (GV_VOL_UP); 321 else 322 return (GV_VOL_DOWN); 323 } 324 325 const char * 326 gv_volstate(int state) 327 { 328 switch (state) { 329 case GV_VOL_UP: 330 return "up"; 331 case GV_VOL_DOWN: 332 return "down"; 333 default: 334 return "??"; 335 } 336 } 337 338 /* Translate from a plex organization to a string. */ 339 const char * 340 gv_plexorg(int org) 341 { 342 switch (org) { 343 case GV_PLEX_DISORG: 344 return "??"; 345 case GV_PLEX_CONCAT: 346 return "concat"; 347 case GV_PLEX_STRIPED: 348 return "striped"; 349 case GV_PLEX_RAID5: 350 return "raid5"; 351 default: 352 return "??"; 353 } 354 } 355 356 const char * 357 gv_plexorg_short(int org) 358 { 359 switch (org) { 360 case GV_PLEX_DISORG: 361 return "??"; 362 case GV_PLEX_CONCAT: 363 return "C"; 364 case GV_PLEX_STRIPED: 365 return "S"; 366 case GV_PLEX_RAID5: 367 return "R5"; 368 default: 369 return "??"; 370 } 371 } 372 373 struct gv_sd * 374 gv_alloc_sd(void) 375 { 376 struct gv_sd *s; 377 378 #ifdef _KERNEL 379 s = g_malloc(sizeof(struct gv_sd), M_NOWAIT); 380 #else 381 s = malloc(sizeof(struct gv_sd)); 382 #endif 383 if (s == NULL) 384 return (NULL); 385 bzero(s, sizeof(struct gv_sd)); 386 s->plex_offset = -1; 387 s->size = -1; 388 s->drive_offset = -1; 389 return (s); 390 } 391 392 struct gv_drive * 393 gv_alloc_drive(void) 394 { 395 struct gv_drive *d; 396 397 #ifdef _KERNEL 398 d = g_malloc(sizeof(struct gv_drive), M_NOWAIT); 399 #else 400 d = malloc(sizeof(struct gv_drive)); 401 #endif 402 if (d == NULL) 403 return (NULL); 404 bzero(d, sizeof(struct gv_drive)); 405 return (d); 406 } 407 408 struct gv_volume * 409 gv_alloc_volume(void) 410 { 411 struct gv_volume *v; 412 413 #ifdef _KERNEL 414 v = g_malloc(sizeof(struct gv_volume), M_NOWAIT); 415 #else 416 v = malloc(sizeof(struct gv_volume)); 417 #endif 418 if (v == NULL) 419 return (NULL); 420 bzero(v, sizeof(struct gv_volume)); 421 return (v); 422 } 423 424 struct gv_plex * 425 gv_alloc_plex(void) 426 { 427 struct gv_plex *p; 428 429 #ifdef _KERNEL 430 p = g_malloc(sizeof(struct gv_plex), M_NOWAIT); 431 #else 432 p = malloc(sizeof(struct gv_plex)); 433 #endif 434 if (p == NULL) 435 return (NULL); 436 bzero(p, sizeof(struct gv_plex)); 437 return (p); 438 } 439 440 /* Get a new drive object. */ 441 struct gv_drive * 442 gv_new_drive(int max, char *token[]) 443 { 444 struct gv_drive *d; 445 int j, errors; 446 char *ptr; 447 448 if (token[1] == NULL || *token[1] == '\0') 449 return (NULL); 450 d = gv_alloc_drive(); 451 if (d == NULL) 452 return (NULL); 453 errors = 0; 454 for (j = 1; j < max; j++) { 455 if (!strcmp(token[j], "state")) { 456 j++; 457 if (j >= max) { 458 errors++; 459 break; 460 } 461 d->state = gv_drivestatei(token[j]); 462 } else if (!strcmp(token[j], "device")) { 463 j++; 464 if (j >= max) { 465 errors++; 466 break; 467 } 468 ptr = token[j]; 469 470 if (strncmp(ptr, _PATH_DEV, 5) == 0) 471 ptr += 5; 472 strlcpy(d->device, ptr, sizeof(d->device)); 473 } else { 474 /* We assume this is the drive name. */ 475 strlcpy(d->name, token[j], sizeof(d->name)); 476 } 477 } 478 479 if (strlen(d->name) == 0 || strlen(d->device) == 0) 480 errors++; 481 482 if (errors) { 483 g_free(d); 484 return (NULL); 485 } 486 487 return (d); 488 } 489 490 /* Get a new volume object. */ 491 struct gv_volume * 492 gv_new_volume(int max, char *token[]) 493 { 494 struct gv_volume *v; 495 int j, errors; 496 497 if (token[1] == NULL || *token[1] == '\0') 498 return (NULL); 499 500 v = gv_alloc_volume(); 501 if (v == NULL) 502 return (NULL); 503 504 errors = 0; 505 for (j = 1; j < max; j++) { 506 if (!strcmp(token[j], "state")) { 507 j++; 508 if (j >= max) { 509 errors++; 510 break; 511 } 512 v->state = gv_volstatei(token[j]); 513 } else { 514 /* We assume this is the volume name. */ 515 strlcpy(v->name, token[j], sizeof(v->name)); 516 } 517 } 518 519 if (strlen(v->name) == 0) 520 errors++; 521 522 if (errors) { 523 g_free(v); 524 return (NULL); 525 } 526 527 return (v); 528 } 529 530 /* Get a new plex object. */ 531 struct gv_plex * 532 gv_new_plex(int max, char *token[]) 533 { 534 struct gv_plex *p; 535 int j, errors; 536 537 if (token[1] == NULL || *token[1] == '\0') 538 return (NULL); 539 540 p = gv_alloc_plex(); 541 if (p == NULL) 542 return (NULL); 543 544 errors = 0; 545 for (j = 1; j < max; j++) { 546 if (!strcmp(token[j], "name")) { 547 j++; 548 if (j >= max) { 549 errors++; 550 break; 551 } 552 strlcpy(p->name, token[j], sizeof(p->name)); 553 } else if (!strcmp(token[j], "org")) { 554 j++; 555 if (j >= max) { 556 errors++; 557 break; 558 } 559 p->org = gv_plexorgi(token[j]); 560 if ((p->org == GV_PLEX_RAID5) || 561 (p->org == GV_PLEX_STRIPED)) { 562 j++; 563 if (j >= max) { 564 errors++; 565 break; 566 } 567 p->stripesize = gv_sizespec(token[j]); 568 if (p->stripesize == 0) { 569 errors++; 570 break; 571 } 572 } 573 } else if (!strcmp(token[j], "state")) { 574 j++; 575 if (j >= max) { 576 errors++; 577 break; 578 } 579 p->state = gv_plexstatei(token[j]); 580 } else if (!strcmp(token[j], "vol") || 581 !strcmp(token[j], "volume")) { 582 j++; 583 if (j >= max) { 584 errors++; 585 break; 586 } 587 strlcpy(p->volume, token[j], sizeof(p->volume)); 588 } else { 589 errors++; 590 break; 591 } 592 } 593 594 if (errors) { 595 g_free(p); 596 return (NULL); 597 } 598 599 return (p); 600 } 601 602 603 604 /* Get a new subdisk object. */ 605 struct gv_sd * 606 gv_new_sd(int max, char *token[]) 607 { 608 struct gv_sd *s; 609 int j, errors; 610 611 if (token[1] == NULL || *token[1] == '\0') 612 return (NULL); 613 614 s = gv_alloc_sd(); 615 if (s == NULL) 616 return (NULL); 617 618 errors = 0; 619 for (j = 1; j < max; j++) { 620 if (!strcmp(token[j], "name")) { 621 j++; 622 if (j >= max) { 623 errors++; 624 break; 625 } 626 strlcpy(s->name, token[j], sizeof(s->name)); 627 } else if (!strcmp(token[j], "drive")) { 628 j++; 629 if (j >= max) { 630 errors++; 631 break; 632 } 633 strlcpy(s->drive, token[j], sizeof(s->drive)); 634 } else if (!strcmp(token[j], "plex")) { 635 j++; 636 if (j >= max) { 637 errors++; 638 break; 639 } 640 strlcpy(s->plex, token[j], sizeof(s->plex)); 641 } else if (!strcmp(token[j], "state")) { 642 j++; 643 if (j >= max) { 644 errors++; 645 break; 646 } 647 s->state = gv_sdstatei(token[j]); 648 } else if (!strcmp(token[j], "len") || 649 !strcmp(token[j], "length")) { 650 j++; 651 if (j >= max) { 652 errors++; 653 break; 654 } 655 s->size = gv_sizespec(token[j]); 656 if (s->size <= 0) 657 s->size = -1; 658 } else if (!strcmp(token[j], "driveoffset")) { 659 j++; 660 if (j >= max) { 661 errors++; 662 break; 663 } 664 s->drive_offset = gv_sizespec(token[j]); 665 if (s->drive_offset != 0 && 666 s->drive_offset < GV_DATA_START) { 667 errors++; 668 break; 669 } 670 } else if (!strcmp(token[j], "plexoffset")) { 671 j++; 672 if (j >= max) { 673 errors++; 674 break; 675 } 676 s->plex_offset = gv_sizespec(token[j]); 677 if (s->plex_offset < 0) { 678 errors++; 679 break; 680 } 681 } else { 682 errors++; 683 break; 684 } 685 } 686 687 if (strlen(s->drive) == 0) 688 errors++; 689 690 if (errors) { 691 g_free(s); 692 return (NULL); 693 } 694 695 return (s); 696 } 697 698 /* 699 * Take a size in bytes and return a pointer to a string which represents the 700 * size best. If lj is != 0, return left justified, otherwise in a fixed 10 701 * character field suitable for columnar printing. 702 * 703 * Note this uses a static string: it's only intended to be used immediately 704 * for printing. 705 */ 706 const char * 707 gv_roughlength(off_t bytes, int lj) 708 { 709 static char desc[16]; 710 711 /* Gigabytes. */ 712 if (bytes > (off_t)MEGABYTE * 10000) 713 snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB", 714 bytes / GIGABYTE); 715 716 /* Megabytes. */ 717 else if (bytes > KILOBYTE * 10000) 718 snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB", 719 bytes / MEGABYTE); 720 721 /* Kilobytes. */ 722 else if (bytes > 10000) 723 snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB", 724 bytes / KILOBYTE); 725 726 /* Bytes. */ 727 else 728 snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes); 729 730 return (desc); 731 } 732