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