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