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