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