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
gv_tokenize(char * cptr,char * token[],int maxtoken)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
gv_sizespec(char * spec)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 *
gv_drivestate(int state)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
gv_drivestatei(char * buf)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
gv_sdstatei(char * buf)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 *
gv_sdstate(int state)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
gv_plexstatei(char * buf)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 *
gv_plexstate(int state)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
gv_plexorgi(char * buf)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
gv_volstatei(char * buf)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 *
gv_volstate(int state)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 *
gv_plexorg(int org)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 *
gv_plexorg_short(int org)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 *
gv_alloc_sd(void)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 *
gv_alloc_drive(void)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 *
gv_alloc_volume(void)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 *
gv_alloc_plex(void)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 *
gv_new_drive(int max,char * token[])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 *
gv_new_volume(int max,char * token[])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 *
gv_new_plex(int max,char * token[])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 *
gv_new_sd(int max,char * token[])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 *
gv_roughlength(off_t bytes,int lj)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