geom_vinum_share.c revision 331722
1/*-
2 * Copyright (c) 2004, 2007 Lukas Ertl
3 * Copyright (c) 1997, 1998, 1999
4 *      Nan Yang Computer Services Limited.  All rights reserved.
5 *
6 *  Parts written by Greg Lehey
7 *
8 *  This software is distributed under the so-called ``Berkeley
9 *  License'':
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *      This product includes software developed by Nan Yang Computer
22 *      Services Limited.
23 * 4. Neither the name of the Company nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * This software is provided ``as is'', and any express or implied
28 * warranties, including, but not limited to, the implied warranties of
29 * merchantability and fitness for a particular purpose are disclaimed.
30 * In no event shall the company or contributors be liable for any
31 * direct, indirect, incidental, special, exemplary, or consequential
32 * damages (including, but not limited to, procurement of substitute
33 * goods or services; loss of use, data, or profits; or business
34 * interruption) however caused and on any theory of liability, whether
35 * in contract, strict liability, or tort (including negligence or
36 * otherwise) arising in any way out of the use of this software, even if
37 * advised of the possibility of such damage.
38 *
39 */
40
41/* This file is shared between kernel and userland. */
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: stable/11/sys/geom/vinum/geom_vinum_share.c 331722 2018-03-29 02:50:57Z eadler $");
45
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 <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 */
75int
76gv_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
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/*
139 * Take a number with an optional scale factor and convert it to a number of
140 * bytes.
141 *
142 * The scale factors are:
143 *
144 * s    sectors (of 512 bytes)
145 * b    blocks (of 512 bytes).  This unit is deprecated, because it's
146 *      confusing, but maintained to avoid confusing Veritas users.
147 * k    kilobytes (1024 bytes)
148 * m    megabytes (of 1024 * 1024 bytes)
149 * g    gigabytes (of 1024 * 1024 * 1024 bytes)
150 *
151 * XXX: need a way to signal error
152 */
153off_t
154gv_sizespec(char *spec)
155{
156	uint64_t size;
157	char *s;
158	int sign;
159
160	size = 0;
161	sign = 1;
162	if (spec != NULL) {		/* we have a parameter */
163		s = spec;
164		if (*s == '-') {	/* negative, */
165			sign = -1;
166			s++;		/* skip */
167		}
168
169		/* It's numeric. */
170		if ((*s >= '0') && (*s <= '9')) {
171
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
205const char *
206gv_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
218int
219gv_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. */
228int
229gv_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. */
244const char *
245gv_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. */
264int
265gv_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. */
280const char *
281gv_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. */
300int
301gv_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
313int
314gv_volstatei(char *buf)
315{
316	if (!strcmp(buf, "up"))
317		return (GV_VOL_UP);
318	else
319		return (GV_VOL_DOWN);
320}
321
322const char *
323gv_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. */
336const char *
337gv_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
353const char *
354gv_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
370struct gv_sd *
371gv_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
389struct gv_drive *
390gv_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
405struct gv_volume *
406gv_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
421struct gv_plex *
422gv_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. */
438struct gv_drive *
439gv_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, "/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. */
488struct gv_volume *
489gv_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. */
528struct gv_plex *
529gv_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
600
601/* Get a new subdisk object. */
602struct gv_sd *
603gv_new_sd(int max, char *token[])
604{
605	struct gv_sd *s;
606	int j, errors;
607
608	if (token[1] == NULL || *token[1] == '\0')
609		return (NULL);
610
611	s = gv_alloc_sd();
612	if (s == NULL)
613		return (NULL);
614
615	errors = 0;
616	for (j = 1; j < max; j++) {
617		if (!strcmp(token[j], "name")) {
618			j++;
619			if (j >= max) {
620				errors++;
621				break;
622			}
623			strlcpy(s->name, token[j], sizeof(s->name));
624		} else if (!strcmp(token[j], "drive")) {
625			j++;
626			if (j >= max) {
627				errors++;
628				break;
629			}
630			strlcpy(s->drive, token[j], sizeof(s->drive));
631		} else if (!strcmp(token[j], "plex")) {
632			j++;
633			if (j >= max) {
634				errors++;
635				break;
636			}
637			strlcpy(s->plex, token[j], sizeof(s->plex));
638		} else if (!strcmp(token[j], "state")) {
639			j++;
640			if (j >= max) {
641				errors++;
642				break;
643			}
644			s->state = gv_sdstatei(token[j]);
645		} else if (!strcmp(token[j], "len") ||
646		    !strcmp(token[j], "length")) {
647			j++;
648			if (j >= max) {
649				errors++;
650				break;
651			}
652			s->size = gv_sizespec(token[j]);
653			if (s->size <= 0)
654				s->size = -1;
655		} else if (!strcmp(token[j], "driveoffset")) {
656			j++;
657			if (j >= max) {
658				errors++;
659				break;
660			}
661			s->drive_offset = gv_sizespec(token[j]);
662			if (s->drive_offset != 0 &&
663			    s->drive_offset < GV_DATA_START) {
664				errors++;
665				break;
666			}
667		} else if (!strcmp(token[j], "plexoffset")) {
668			j++;
669			if (j >= max) {
670				errors++;
671				break;
672			}
673			s->plex_offset = gv_sizespec(token[j]);
674			if (s->plex_offset < 0) {
675				errors++;
676				break;
677			}
678		} else {
679			errors++;
680			break;
681		}
682	}
683
684	if (strlen(s->drive) == 0)
685		errors++;
686
687	if (errors) {
688		g_free(s);
689		return (NULL);
690	}
691
692	return (s);
693}
694
695/*
696 * Take a size in bytes and return a pointer to a string which represents the
697 * size best.  If lj is != 0, return left justified, otherwise in a fixed 10
698 * character field suitable for columnar printing.
699 *
700 * Note this uses a static string: it's only intended to be used immediately
701 * for printing.
702 */
703const char *
704gv_roughlength(off_t bytes, int lj)
705{
706	static char desc[16];
707
708	/* Gigabytes. */
709	if (bytes > (off_t)MEGABYTE * 10000)
710		snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB",
711		    bytes / GIGABYTE);
712
713	/* Megabytes. */
714	else if (bytes > KILOBYTE * 10000)
715		snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB",
716		    bytes / MEGABYTE);
717
718	/* Kilobytes. */
719	else if (bytes > 10000)
720		snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB",
721		    bytes / KILOBYTE);
722
723	/* Bytes. */
724	else
725		snprintf(desc, sizeof(desc), lj ? "%jd  B" : "%10jd  B", bytes);
726
727	return (desc);
728}
729