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