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