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