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