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