xref: /freebsd/sys/geom/vinum/geom_vinum_share.c (revision f7c4bd95ba735bd6a5454b4953945a99cefbb80c)
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$");
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  */
80 int
81 gv_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  */
158 off_t
159 gv_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 
210 const char *
211 gv_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 
223 int
224 gv_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. */
233 int
234 gv_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. */
247 const char *
248 gv_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. */
267 int
268 gv_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. */
281 const char *
282 gv_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. */
299 int
300 gv_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 
312 int
313 gv_volstatei(char *buf)
314 {
315 	if (!strcmp(buf, "up"))
316 		return (GV_VOL_UP);
317 	else
318 		return (GV_VOL_DOWN);
319 }
320 
321 const char *
322 gv_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. */
335 const char *
336 gv_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 
352 const char *
353 gv_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. */
370 struct gv_drive *
371 gv_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 
407 			if (strncmp(ptr, "/dev/", 5) == 0)
408 				ptr += 5;
409 			strncpy(d->device, ptr, GV_MAXDRIVENAME);
410 		} else {
411 			/* We assume this is the drive name. */
412 			strncpy(d->name, token[j], GV_MAXDRIVENAME);
413 		}
414 	}
415 
416 	if (strlen(d->name) == 0 || strlen(d->device) == 0)
417 		errors++;
418 
419 	if (errors) {
420 		g_free(d);
421 		return (NULL);
422 	}
423 
424 	return (d);
425 }
426 
427 /* Get a new volume object. */
428 struct gv_volume *
429 gv_new_volume(int max, char *token[])
430 {
431 	struct gv_volume *v;
432 	int j, errors;
433 
434 	if (token[1] == NULL || *token[1] == '\0')
435 		return (NULL);
436 
437 #ifdef _KERNEL
438 	v = g_malloc(sizeof(struct gv_volume), M_WAITOK | M_ZERO);
439 
440 #else
441 	v = malloc(sizeof(struct gv_volume));
442 	if (v == NULL)
443 		return (NULL);
444 	bzero(v, sizeof(struct gv_volume));
445 #endif
446 
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 			v->state = gv_volstatei(token[j]);
456 		} else {
457 			/* We assume this is the volume name. */
458 			strncpy(v->name, token[j], GV_MAXVOLNAME);
459 		}
460 	}
461 
462 	if (strlen(v->name) == 0)
463 		errors++;
464 
465 	if (errors) {
466 		g_free(v);
467 		return (NULL);
468 	}
469 
470 	return (v);
471 }
472 
473 /* Get a new plex object. */
474 struct gv_plex *
475 gv_new_plex(int max, char *token[])
476 {
477 	struct gv_plex *p;
478 	int j, errors;
479 
480 	if (token[1] == NULL || *token[1] == '\0')
481 		return (NULL);
482 
483 #ifdef _KERNEL
484 	p = g_malloc(sizeof(struct gv_plex), M_WAITOK | M_ZERO);
485 #else
486 	p = malloc(sizeof(struct gv_plex));
487 	if (p == NULL)
488 		return (NULL);
489 	bzero(p, sizeof(struct gv_plex));
490 #endif
491 
492 	errors = 0;
493 	for (j = 1; j < max; j++) {
494 		if (!strcmp(token[j], "name")) {
495 			j++;
496 			if (j >= max) {
497 				errors++;
498 				break;
499 			}
500 			strncpy(p->name, token[j], GV_MAXPLEXNAME);
501 		} else if (!strcmp(token[j], "org")) {
502 			j++;
503 			if (j >= max) {
504 				errors++;
505 				break;
506 			}
507 			p->org = gv_plexorgi(token[j]);
508 			if ((p->org == GV_PLEX_RAID5) ||
509 			    (p->org == GV_PLEX_STRIPED)) {
510 				j++;
511 				if (j >= max) {
512 					errors++;
513 					break;
514 				}
515 				p->stripesize = gv_sizespec(token[j]);
516 				if (p->stripesize == 0) {
517 					errors++;
518 					break;
519 				}
520 			}
521 		} else if (!strcmp(token[j], "state")) {
522 			j++;
523 			if (j >= max) {
524 				errors++;
525 				break;
526 			}
527 			p->state = gv_plexstatei(token[j]);
528 		} else if (!strcmp(token[j], "vol") ||
529 			    !strcmp(token[j], "volume")) {
530 			j++;
531 			if (j >= max) {
532 				errors++;
533 				break;
534 			}
535 			strncpy(p->volume, token[j], GV_MAXVOLNAME);
536 		} else {
537 			errors++;
538 			break;
539 		}
540 	}
541 
542 	if (errors) {
543 		g_free(p);
544 		return (NULL);
545 	}
546 
547 	return (p);
548 }
549 
550 /* Get a new subdisk object. */
551 struct gv_sd *
552 gv_new_sd(int max, char *token[])
553 {
554 	struct gv_sd *s;
555 	int j, errors;
556 
557 	if (token[1] == NULL || *token[1] == '\0')
558 		return NULL;
559 
560 #ifdef _KERNEL
561 	s = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO);
562 #else
563 	s = malloc(sizeof(struct gv_sd));
564 	if (s == NULL)
565 		return NULL;
566 	bzero(s, sizeof(struct gv_sd));
567 #endif
568 
569 	s->plex_offset = -1;
570 	s->size = -1;
571 	s->drive_offset = -1;
572 	errors = 0;
573 	for (j = 1; j < max; j++) {
574 		if (!strcmp(token[j], "name")) {
575 			j++;
576 			if (j >= max) {
577 				errors++;
578 				break;
579 			}
580 			strncpy(s->name, token[j], GV_MAXSDNAME);
581 		} else if (!strcmp(token[j], "drive")) {
582 			j++;
583 			if (j >= max) {
584 				errors++;
585 				break;
586 			}
587 			strncpy(s->drive, token[j], GV_MAXDRIVENAME);
588 		} else if (!strcmp(token[j], "plex")) {
589 			j++;
590 			if (j >= max) {
591 				errors++;
592 				break;
593 			}
594 			strncpy(s->plex, token[j], GV_MAXPLEXNAME);
595 		} else if (!strcmp(token[j], "state")) {
596 			j++;
597 			if (j >= max) {
598 				errors++;
599 				break;
600 			}
601 			s->state = gv_sdstatei(token[j]);
602 		} else if (!strcmp(token[j], "len") ||
603 		    !strcmp(token[j], "length")) {
604 			j++;
605 			if (j >= max) {
606 				errors++;
607 				break;
608 			}
609 			s->size = gv_sizespec(token[j]);
610 			if (s->size <= 0)
611 				s->size = -1;
612 		} else if (!strcmp(token[j], "driveoffset")) {
613 			j++;
614 			if (j >= max) {
615 				errors++;
616 				break;
617 			}
618 			s->drive_offset = gv_sizespec(token[j]);
619 			if (s->drive_offset != 0 &&
620 			    s->drive_offset < GV_DATA_START) {
621 				errors++;
622 				break;
623 			}
624 		} else if (!strcmp(token[j], "plexoffset")) {
625 			j++;
626 			if (j >= max) {
627 				errors++;
628 				break;
629 			}
630 			s->plex_offset = gv_sizespec(token[j]);
631 			if (s->plex_offset < 0) {
632 				errors++;
633 				break;
634 			}
635 		} else {
636 			errors++;
637 			break;
638 		}
639 	}
640 
641 	if (strlen(s->drive) == 0)
642 		errors++;
643 
644 	if (errors) {
645 		g_free(s);
646 		return (NULL);
647 	}
648 
649 	return (s);
650 }
651 
652 /*
653  * Take a size in bytes and return a pointer to a string which represents the
654  * size best.  If lj is != 0, return left justified, otherwise in a fixed 10
655  * character field suitable for columnar printing.
656  *
657  * Note this uses a static string: it's only intended to be used immediately
658  * for printing.
659  */
660 const char *
661 gv_roughlength(off_t bytes, int lj)
662 {
663 	static char desc[16];
664 
665 	/* Gigabytes. */
666 	if (bytes > (off_t)MEGABYTE * 10000)
667 		snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB",
668 		    bytes / GIGABYTE);
669 
670 	/* Megabytes. */
671 	else if (bytes > KILOBYTE * 10000)
672 		snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB",
673 		    bytes / MEGABYTE);
674 
675 	/* Kilobytes. */
676 	else if (bytes > 10000)
677 		snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB",
678 		    bytes / KILOBYTE);
679 
680 	/* Bytes. */
681 	else
682 		snprintf(desc, sizeof(desc), lj ? "%jd  B" : "%10jd  B", bytes);
683 
684 	return (desc);
685 }
686