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