xref: /freebsd/sys/geom/vinum/geom_vinum_share.c (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
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 >= '0') && (*s <= '9')) {
172 			/* It's numeric. */
173 			while ((*s >= '0') && (*s <= '9'))
174 				/* Convert it. */
175 				size = size * 10 + *s++ - '0';
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 }
727