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 >= '') && (*s <= '9')) {
172 /* It's numeric. */
173 while ((*s >= '') && (*s <= '9'))
174 /* Convert it. */
175 size = size * 10 + *s++ - '';
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 }
Cache object: ecca1e9d19e06f2e2fbc80e443736da2
|