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