1130389Sle/*-
2190507Slulf * Copyright (c) 2004, 2007 Lukas Ertl
3130389Sle * Copyright (c) 1997, 1998, 1999
4130389Sle *      Nan Yang Computer Services Limited.  All rights reserved.
5130389Sle *
6130389Sle *  Parts written by Greg Lehey
7130389Sle *
8130389Sle *  This software is distributed under the so-called ``Berkeley
9130389Sle *  License'':
10130389Sle *
11130389Sle * Redistribution and use in source and binary forms, with or without
12130389Sle * modification, are permitted provided that the following conditions
13130389Sle * are met:
14130389Sle * 1. Redistributions of source code must retain the above copyright
15130389Sle *    notice, this list of conditions and the following disclaimer.
16130389Sle * 2. Redistributions in binary form must reproduce the above copyright
17130389Sle *    notice, this list of conditions and the following disclaimer in the
18130389Sle *    documentation and/or other materials provided with the distribution.
19130389Sle * 3. All advertising materials mentioning features or use of this software
20130389Sle *    must display the following acknowledgement:
21130389Sle *      This product includes software developed by Nan Yang Computer
22130389Sle *      Services Limited.
23130389Sle * 4. Neither the name of the Company nor the names of its contributors
24130389Sle *    may be used to endorse or promote products derived from this software
25130389Sle *    without specific prior written permission.
26130389Sle *
27130389Sle * This software is provided ``as is'', and any express or implied
28130389Sle * warranties, including, but not limited to, the implied warranties of
29130389Sle * merchantability and fitness for a particular purpose are disclaimed.
30130389Sle * In no event shall the company or contributors be liable for any
31130389Sle * direct, indirect, incidental, special, exemplary, or consequential
32130389Sle * damages (including, but not limited to, procurement of substitute
33130389Sle * goods or services; loss of use, data, or profits; or business
34130389Sle * interruption) however caused and on any theory of liability, whether
35130389Sle * in contract, strict liability, or tort (including negligence or
36130389Sle * otherwise) arising in any way out of the use of this software, even if
37130389Sle * advised of the possibility of such damage.
38130389Sle *
39130389Sle */
40130389Sle
41130389Sle/* This file is shared between kernel and userland. */
42130389Sle
43130389Sle#include <sys/cdefs.h>
44130389Sle__FBSDID("$FreeBSD$");
45130389Sle
46130389Sle#include <sys/param.h>
47130389Sle#ifdef _KERNEL
48130389Sle#include <sys/malloc.h>
49130389Sle#include <sys/systm.h>
50130389Sle
51130389Sle#include <geom/geom.h>
52130389Sle#define	iswhite(c) (((c) == ' ') || ((c) == '\t'))
53130389Sle#else
54130389Sle#include <ctype.h>
55130389Sle#include <stdio.h>
56130389Sle#include <stdlib.h>
57130389Sle#include <string.h>
58130389Sle#define	iswhite	isspace
59130389Sle#define	g_free	free
60130389Sle#endif /* _KERNEL */
61130389Sle
62130389Sle#include <sys/mutex.h>
63130389Sle#include <sys/queue.h>
64130389Sle
65130389Sle#include <geom/vinum/geom_vinum_var.h>
66130389Sle#include <geom/vinum/geom_vinum_share.h>
67130389Sle
68130389Sle/*
69130389Sle * Take a blank separated list of tokens and turn it into a list of
70130389Sle * individual nul-delimited strings.  Build a list of pointers at
71130389Sle * token, which must have enough space for the tokens.  Return the
72130389Sle * number of tokens, or -1 on error (typically a missing string
73130389Sle * delimiter).
74130389Sle */
75130389Sleint
76130389Slegv_tokenize(char *cptr, char *token[], int maxtoken)
77130389Sle{
78130389Sle	int tokennr;	/* Index of this token. */
79130389Sle	char delim;	/* Delimiter for searching for the partner. */
80130389Sle
81130389Sle	for (tokennr = 0; tokennr < maxtoken;) {
82130389Sle
83130389Sle		/* Skip leading white space. */
84130389Sle		while (iswhite(*cptr))
85130389Sle			cptr++;
86130389Sle
87130389Sle		/* End of line. */
88130389Sle		if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#'))
89130389Sle			return tokennr;
90130389Sle
91130389Sle		delim = *cptr;
92130389Sle		token[tokennr] = cptr;		/* Point to it. */
93130389Sle		tokennr++;			/* One more. */
94130389Sle
95130389Sle		/* Run off the end? */
96130389Sle		if (tokennr == maxtoken)
97130389Sle			return tokennr;
98130389Sle
99130389Sle		/* Quoted? */
100130389Sle		if ((delim == '\'') || (delim == '"')) {
101130389Sle			for (;;) {
102130389Sle				cptr++;
103130389Sle
104130389Sle				/* Found the partner. */
105130389Sle				if ((*cptr == delim) && (cptr[-1] != '\\')) {
106130389Sle					cptr++;
107130389Sle
108130389Sle					/* Space after closing quote needed. */
109130389Sle					if (!iswhite(*cptr))
110130389Sle						return -1;
111130389Sle
112130389Sle					/* Delimit. */
113130389Sle					*cptr++ = '\0';
114130389Sle
115130389Sle				/* End-of-line? */
116130389Sle				} else if ((*cptr == '\0') || (*cptr == '\n'))
117130389Sle					return -1;
118130389Sle			}
119130389Sle
120130389Sle		/* Not quoted. */
121130389Sle		} else {
122130389Sle			while ((*cptr != '\0') &&
123130389Sle			    (!iswhite(*cptr)) &&
124130389Sle			    (*cptr != '\n'))
125130389Sle				cptr++;
126130389Sle
127130389Sle			/* Not end-of-line; delimit and move to the next. */
128130389Sle			if (*cptr != '\0')
129130389Sle				*cptr++ = '\0';
130130389Sle		}
131130389Sle	}
132130389Sle
133130389Sle	/* Can't get here. */
134130389Sle	return maxtoken;
135130389Sle}
136130389Sle
137130389Sle
138130389Sle/*
139130389Sle * Take a number with an optional scale factor and convert it to a number of
140130389Sle * bytes.
141130389Sle *
142130389Sle * The scale factors are:
143130389Sle *
144130389Sle * s    sectors (of 512 bytes)
145130389Sle * b    blocks (of 512 bytes).  This unit is deprecated, because it's
146130389Sle *      confusing, but maintained to avoid confusing Veritas users.
147130389Sle * k    kilobytes (1024 bytes)
148130389Sle * m    megabytes (of 1024 * 1024 bytes)
149130389Sle * g    gigabytes (of 1024 * 1024 * 1024 bytes)
150130389Sle *
151130389Sle * XXX: need a way to signal error
152130389Sle */
153130389Sleoff_t
154130389Slegv_sizespec(char *spec)
155130389Sle{
156130389Sle	uint64_t size;
157130389Sle	char *s;
158130389Sle	int sign;
159130389Sle
160130389Sle	size = 0;
161130389Sle	sign = 1;
162130389Sle	if (spec != NULL) {		/* we have a parameter */
163130389Sle		s = spec;
164130389Sle		if (*s == '-') {	/* negative, */
165130389Sle			sign = -1;
166130389Sle			s++;		/* skip */
167130389Sle		}
168130389Sle
169130389Sle		/* It's numeric. */
170130389Sle		if ((*s >= '0') && (*s <= '9')) {
171130389Sle
172130389Sle			/* It's numeric. */
173130389Sle			while ((*s >= '0') && (*s <= '9'))
174130389Sle				/* Convert it. */
175130389Sle				size = size * 10 + *s++ - '0';
176130389Sle
177130389Sle			switch (*s) {
178130389Sle			case '\0':
179130389Sle				return size * sign;
180130389Sle
181130389Sle			case 'B':
182130389Sle			case 'b':
183130389Sle			case 'S':
184130389Sle			case 's':
185130389Sle				return size * sign * 512;
186130389Sle
187130389Sle			case 'K':
188130389Sle			case 'k':
189130389Sle				return size * sign * 1024;
190130389Sle
191130389Sle			case 'M':
192130389Sle			case 'm':
193130389Sle				return size * sign * 1024 * 1024;
194130389Sle
195130389Sle			case 'G':
196130389Sle			case 'g':
197130389Sle				return size * sign * 1024 * 1024 * 1024;
198130389Sle			}
199130389Sle		}
200130389Sle	}
201130389Sle
202130389Sle	return (0);
203130389Sle}
204130389Sle
205130389Sleconst char *
206130389Slegv_drivestate(int state)
207130389Sle{
208130389Sle	switch (state) {
209130389Sle	case GV_DRIVE_DOWN:
210130389Sle		return "down";
211130389Sle	case GV_DRIVE_UP:
212130389Sle		return "up";
213130389Sle	default:
214130389Sle		return "??";
215130389Sle	}
216130389Sle}
217130389Sle
218130389Sleint
219130389Slegv_drivestatei(char *buf)
220130389Sle{
221130389Sle	if (!strcmp(buf, "up"))
222130389Sle		return (GV_DRIVE_UP);
223130389Sle	else
224130389Sle		return (GV_DRIVE_DOWN);
225130389Sle}
226130389Sle
227130389Sle/* Translate from a string to a subdisk state. */
228130389Sleint
229130389Slegv_sdstatei(char *buf)
230130389Sle{
231130389Sle	if (!strcmp(buf, "up"))
232130389Sle		return (GV_SD_UP);
233130389Sle	else if (!strcmp(buf, "reviving"))
234130389Sle		return (GV_SD_REVIVING);
235190507Slulf	else if (!strcmp(buf, "initializing"))
236190507Slulf		return (GV_SD_INITIALIZING);
237130389Sle	else if (!strcmp(buf, "stale"))
238130389Sle		return (GV_SD_STALE);
239130389Sle	else
240130389Sle		return (GV_SD_DOWN);
241130389Sle}
242130389Sle
243130389Sle/* Translate from a subdisk state to a string. */
244130389Sleconst char *
245130389Slegv_sdstate(int state)
246130389Sle{
247130389Sle	switch (state) {
248130389Sle	case GV_SD_INITIALIZING:
249130389Sle		return "initializing";
250130389Sle	case GV_SD_STALE:
251130389Sle		return "stale";
252130389Sle	case GV_SD_DOWN:
253130389Sle		return "down";
254130389Sle	case GV_SD_REVIVING:
255130389Sle		return "reviving";
256130389Sle	case GV_SD_UP:
257130389Sle		return "up";
258130389Sle	default:
259130389Sle		return "??";
260130389Sle	}
261130389Sle}
262130389Sle
263130389Sle/* Translate from a string to a plex state. */
264130389Sleint
265130389Slegv_plexstatei(char *buf)
266130389Sle{
267130389Sle	if (!strcmp(buf, "up"))
268130389Sle		return (GV_PLEX_UP);
269130389Sle	else if (!strcmp(buf, "initializing"))
270130389Sle		return (GV_PLEX_INITIALIZING);
271130389Sle	else if (!strcmp(buf, "degraded"))
272130389Sle		return (GV_PLEX_DEGRADED);
273190507Slulf	else if (!strcmp(buf, "growable"))
274190507Slulf		return (GV_PLEX_GROWABLE);
275130389Sle	else
276130389Sle		return (GV_PLEX_DOWN);
277130389Sle}
278130389Sle
279130389Sle/* Translate from a plex state to a string. */
280130389Sleconst char *
281130389Slegv_plexstate(int state)
282130389Sle{
283130389Sle	switch (state) {
284130389Sle	case GV_PLEX_DOWN:
285130389Sle		return "down";
286130389Sle	case GV_PLEX_INITIALIZING:
287130389Sle		return "initializing";
288130389Sle	case GV_PLEX_DEGRADED:
289130389Sle		return "degraded";
290190507Slulf	case GV_PLEX_GROWABLE:
291190507Slulf		return "growable";
292130389Sle	case GV_PLEX_UP:
293130389Sle		return "up";
294130389Sle	default:
295130389Sle		return "??";
296130389Sle	}
297130389Sle}
298130389Sle
299130389Sle/* Translate from a string to a plex organization. */
300130389Sleint
301130389Slegv_plexorgi(char *buf)
302130389Sle{
303130389Sle	if (!strcmp(buf, "concat"))
304130389Sle		return (GV_PLEX_CONCAT);
305130389Sle	else if (!strcmp(buf, "striped"))
306130389Sle		return (GV_PLEX_STRIPED);
307130389Sle	else if (!strcmp(buf, "raid5"))
308130389Sle		return (GV_PLEX_RAID5);
309130389Sle	else
310130389Sle		return (GV_PLEX_DISORG);
311130389Sle}
312130389Sle
313130389Sleint
314130389Slegv_volstatei(char *buf)
315130389Sle{
316130389Sle	if (!strcmp(buf, "up"))
317130389Sle		return (GV_VOL_UP);
318130389Sle	else
319130389Sle		return (GV_VOL_DOWN);
320130389Sle}
321130389Sle
322130389Sleconst char *
323130389Slegv_volstate(int state)
324130389Sle{
325130389Sle	switch (state) {
326130389Sle	case GV_VOL_UP:
327130389Sle		return "up";
328130389Sle	case GV_VOL_DOWN:
329130389Sle		return "down";
330130389Sle	default:
331130389Sle		return "??";
332130389Sle	}
333130389Sle}
334130389Sle
335130389Sle/* Translate from a plex organization to a string. */
336130389Sleconst char *
337130389Slegv_plexorg(int org)
338130389Sle{
339130389Sle	switch (org) {
340130389Sle	case GV_PLEX_DISORG:
341130389Sle		return "??";
342130389Sle	case GV_PLEX_CONCAT:
343130389Sle		return "concat";
344130389Sle	case GV_PLEX_STRIPED:
345130389Sle		return "striped";
346130389Sle	case GV_PLEX_RAID5:
347130389Sle		return "raid5";
348130389Sle	default:
349130389Sle		return "??";
350130389Sle	}
351130389Sle}
352130389Sle
353130389Sleconst char *
354130389Slegv_plexorg_short(int org)
355130389Sle{
356130389Sle	switch (org) {
357130389Sle	case GV_PLEX_DISORG:
358130389Sle		return "??";
359130389Sle	case GV_PLEX_CONCAT:
360130389Sle		return "C";
361130389Sle	case GV_PLEX_STRIPED:
362130389Sle		return "S";
363130389Sle	case GV_PLEX_RAID5:
364130389Sle		return "R5";
365130389Sle	default:
366130389Sle		return "??";
367130389Sle	}
368130389Sle}
369130389Sle
370190881Slulfstruct gv_sd *
371190881Slulfgv_alloc_sd(void)
372190881Slulf{
373190881Slulf	struct gv_sd *s;
374190881Slulf
375190881Slulf#ifdef _KERNEL
376190881Slulf	s = g_malloc(sizeof(struct gv_sd), M_NOWAIT);
377190881Slulf#else
378190881Slulf	s = malloc(sizeof(struct gv_sd));
379190881Slulf#endif
380190881Slulf	if (s == NULL)
381190881Slulf		return (NULL);
382190881Slulf	bzero(s, sizeof(struct gv_sd));
383190881Slulf	s->plex_offset = -1;
384190881Slulf	s->size = -1;
385190881Slulf	s->drive_offset = -1;
386190881Slulf	return (s);
387190881Slulf}
388190881Slulf
389130389Slestruct gv_drive *
390190881Slulfgv_alloc_drive(void)
391130389Sle{
392130389Sle	struct gv_drive *d;
393130389Sle
394130389Sle#ifdef _KERNEL
395190507Slulf	d = g_malloc(sizeof(struct gv_drive), M_NOWAIT);
396130389Sle#else
397130389Sle	d = malloc(sizeof(struct gv_drive));
398190507Slulf#endif
399130389Sle	if (d == NULL)
400130389Sle		return (NULL);
401130389Sle	bzero(d, sizeof(struct gv_drive));
402190881Slulf	return (d);
403190881Slulf}
404130389Sle
405190881Slulfstruct gv_volume *
406190881Slulfgv_alloc_volume(void)
407190881Slulf{
408190881Slulf	struct gv_volume *v;
409190881Slulf
410190881Slulf#ifdef _KERNEL
411190881Slulf	v = g_malloc(sizeof(struct gv_volume), M_NOWAIT);
412190881Slulf#else
413190881Slulf	v = malloc(sizeof(struct gv_volume));
414190881Slulf#endif
415190881Slulf	if (v == NULL)
416190881Slulf		return (NULL);
417190881Slulf	bzero(v, sizeof(struct gv_volume));
418190881Slulf	return (v);
419190881Slulf}
420190881Slulf
421190881Slulfstruct gv_plex *
422190881Slulfgv_alloc_plex(void)
423190881Slulf{
424190881Slulf	struct gv_plex *p;
425190881Slulf
426190881Slulf#ifdef _KERNEL
427190881Slulf	p = g_malloc(sizeof(struct gv_plex), M_NOWAIT);
428190881Slulf#else
429190881Slulf	p = malloc(sizeof(struct gv_plex));
430190881Slulf#endif
431190881Slulf	if (p == NULL)
432190881Slulf		return (NULL);
433190881Slulf	bzero(p, sizeof(struct gv_plex));
434190881Slulf	return (p);
435190881Slulf}
436190881Slulf
437190881Slulf/* Get a new drive object. */
438190881Slulfstruct gv_drive *
439190881Slulfgv_new_drive(int max, char *token[])
440190881Slulf{
441190881Slulf	struct gv_drive *d;
442190881Slulf	int j, errors;
443190881Slulf	char *ptr;
444190881Slulf
445190881Slulf	if (token[1] == NULL || *token[1] == '\0')
446190881Slulf		return (NULL);
447190881Slulf	d = gv_alloc_drive();
448190881Slulf	if (d == NULL)
449190881Slulf		return (NULL);
450130389Sle	errors = 0;
451130389Sle	for (j = 1; j < max; j++) {
452130389Sle		if (!strcmp(token[j], "state")) {
453130389Sle			j++;
454130389Sle			if (j >= max) {
455130389Sle				errors++;
456130389Sle				break;
457130389Sle			}
458130389Sle			d->state = gv_drivestatei(token[j]);
459130389Sle		} else if (!strcmp(token[j], "device")) {
460130389Sle			j++;
461130389Sle			if (j >= max) {
462130389Sle				errors++;
463130389Sle				break;
464130389Sle			}
465136064Sle			ptr = token[j];
466168669Sle
467168669Sle			if (strncmp(ptr, "/dev/", 5) == 0)
468168669Sle				ptr += 5;
469190507Slulf			strlcpy(d->device, ptr, sizeof(d->device));
470130389Sle		} else {
471130389Sle			/* We assume this is the drive name. */
472190507Slulf			strlcpy(d->name, token[j], sizeof(d->name));
473130389Sle		}
474130389Sle	}
475130389Sle
476130389Sle	if (strlen(d->name) == 0 || strlen(d->device) == 0)
477130389Sle		errors++;
478130389Sle
479130389Sle	if (errors) {
480130389Sle		g_free(d);
481130389Sle		return (NULL);
482130389Sle	}
483130389Sle
484130389Sle	return (d);
485130389Sle}
486130389Sle
487130389Sle/* Get a new volume object. */
488130389Slestruct gv_volume *
489130389Slegv_new_volume(int max, char *token[])
490130389Sle{
491130389Sle	struct gv_volume *v;
492130389Sle	int j, errors;
493130389Sle
494130389Sle	if (token[1] == NULL || *token[1] == '\0')
495130389Sle		return (NULL);
496130389Sle
497190881Slulf	v = gv_alloc_volume();
498130389Sle	if (v == NULL)
499130389Sle		return (NULL);
500130389Sle
501130389Sle	errors = 0;
502130389Sle	for (j = 1; j < max; j++) {
503130389Sle		if (!strcmp(token[j], "state")) {
504130389Sle			j++;
505130389Sle			if (j >= max) {
506130389Sle				errors++;
507130389Sle				break;
508130389Sle			}
509130389Sle			v->state = gv_volstatei(token[j]);
510130389Sle		} else {
511130389Sle			/* We assume this is the volume name. */
512190507Slulf			strlcpy(v->name, token[j], sizeof(v->name));
513130389Sle		}
514130389Sle	}
515130389Sle
516130389Sle	if (strlen(v->name) == 0)
517130389Sle		errors++;
518130389Sle
519130389Sle	if (errors) {
520130389Sle		g_free(v);
521130389Sle		return (NULL);
522130389Sle	}
523130389Sle
524130389Sle	return (v);
525130389Sle}
526130389Sle
527130389Sle/* Get a new plex object. */
528130389Slestruct gv_plex *
529130389Slegv_new_plex(int max, char *token[])
530130389Sle{
531130389Sle	struct gv_plex *p;
532130389Sle	int j, errors;
533130389Sle
534130389Sle	if (token[1] == NULL || *token[1] == '\0')
535130389Sle		return (NULL);
536130389Sle
537190881Slulf	p = gv_alloc_plex();
538130389Sle	if (p == NULL)
539130389Sle		return (NULL);
540130389Sle
541130389Sle	errors = 0;
542130389Sle	for (j = 1; j < max; j++) {
543130389Sle		if (!strcmp(token[j], "name")) {
544130389Sle			j++;
545130389Sle			if (j >= max) {
546130389Sle				errors++;
547130389Sle				break;
548130389Sle			}
549190507Slulf			strlcpy(p->name, token[j], sizeof(p->name));
550130389Sle		} else if (!strcmp(token[j], "org")) {
551130389Sle			j++;
552130389Sle			if (j >= max) {
553130389Sle				errors++;
554130389Sle				break;
555130389Sle			}
556130389Sle			p->org = gv_plexorgi(token[j]);
557130389Sle			if ((p->org == GV_PLEX_RAID5) ||
558130389Sle			    (p->org == GV_PLEX_STRIPED)) {
559130389Sle				j++;
560130389Sle				if (j >= max) {
561130389Sle					errors++;
562130389Sle					break;
563130389Sle				}
564130389Sle				p->stripesize = gv_sizespec(token[j]);
565130389Sle				if (p->stripesize == 0) {
566130389Sle					errors++;
567130389Sle					break;
568130389Sle				}
569130389Sle			}
570130389Sle		} else if (!strcmp(token[j], "state")) {
571130389Sle			j++;
572130389Sle			if (j >= max) {
573130389Sle				errors++;
574130389Sle				break;
575130389Sle			}
576130389Sle			p->state = gv_plexstatei(token[j]);
577179206Slulf		} else if (!strcmp(token[j], "vol") ||
578179206Slulf			    !strcmp(token[j], "volume")) {
579130389Sle			j++;
580130389Sle			if (j >= max) {
581130389Sle				errors++;
582130389Sle				break;
583130389Sle			}
584190507Slulf			strlcpy(p->volume, token[j], sizeof(p->volume));
585130389Sle		} else {
586130389Sle			errors++;
587130389Sle			break;
588130389Sle		}
589130389Sle	}
590130389Sle
591130389Sle	if (errors) {
592130389Sle		g_free(p);
593130389Sle		return (NULL);
594130389Sle	}
595130389Sle
596130389Sle	return (p);
597130389Sle}
598130389Sle
599190881Slulf
600190881Slulf
601130389Sle/* Get a new subdisk object. */
602130389Slestruct gv_sd *
603130389Slegv_new_sd(int max, char *token[])
604130389Sle{
605130389Sle	struct gv_sd *s;
606130389Sle	int j, errors;
607130389Sle
608130389Sle	if (token[1] == NULL || *token[1] == '\0')
609190507Slulf		return (NULL);
610130389Sle
611190881Slulf	s = gv_alloc_sd();
612130389Sle	if (s == NULL)
613190507Slulf		return (NULL);
614130389Sle
615130389Sle	errors = 0;
616130389Sle	for (j = 1; j < max; j++) {
617130389Sle		if (!strcmp(token[j], "name")) {
618130389Sle			j++;
619130389Sle			if (j >= max) {
620130389Sle				errors++;
621130389Sle				break;
622130389Sle			}
623190507Slulf			strlcpy(s->name, token[j], sizeof(s->name));
624130389Sle		} else if (!strcmp(token[j], "drive")) {
625130389Sle			j++;
626130389Sle			if (j >= max) {
627130389Sle				errors++;
628130389Sle				break;
629130389Sle			}
630190507Slulf			strlcpy(s->drive, token[j], sizeof(s->drive));
631130389Sle		} else if (!strcmp(token[j], "plex")) {
632130389Sle			j++;
633130389Sle			if (j >= max) {
634130389Sle				errors++;
635130389Sle				break;
636130389Sle			}
637190507Slulf			strlcpy(s->plex, token[j], sizeof(s->plex));
638130389Sle		} else if (!strcmp(token[j], "state")) {
639130389Sle			j++;
640130389Sle			if (j >= max) {
641130389Sle				errors++;
642130389Sle				break;
643130389Sle			}
644130389Sle			s->state = gv_sdstatei(token[j]);
645130389Sle		} else if (!strcmp(token[j], "len") ||
646130389Sle		    !strcmp(token[j], "length")) {
647130389Sle			j++;
648130389Sle			if (j >= max) {
649130389Sle				errors++;
650130389Sle				break;
651130389Sle			}
652130389Sle			s->size = gv_sizespec(token[j]);
653130990Sle			if (s->size <= 0)
654130990Sle				s->size = -1;
655130389Sle		} else if (!strcmp(token[j], "driveoffset")) {
656130389Sle			j++;
657130389Sle			if (j >= max) {
658130389Sle				errors++;
659130389Sle				break;
660130389Sle			}
661130389Sle			s->drive_offset = gv_sizespec(token[j]);
662130389Sle			if (s->drive_offset != 0 &&
663130389Sle			    s->drive_offset < GV_DATA_START) {
664130389Sle				errors++;
665130389Sle				break;
666130389Sle			}
667130389Sle		} else if (!strcmp(token[j], "plexoffset")) {
668130389Sle			j++;
669130389Sle			if (j >= max) {
670130389Sle				errors++;
671130389Sle				break;
672130389Sle			}
673130389Sle			s->plex_offset = gv_sizespec(token[j]);
674130389Sle			if (s->plex_offset < 0) {
675130389Sle				errors++;
676130389Sle				break;
677130389Sle			}
678130389Sle		} else {
679130389Sle			errors++;
680130389Sle			break;
681130389Sle		}
682130389Sle	}
683130389Sle
684130389Sle	if (strlen(s->drive) == 0)
685130389Sle		errors++;
686130389Sle
687130389Sle	if (errors) {
688130389Sle		g_free(s);
689130389Sle		return (NULL);
690130389Sle	}
691130389Sle
692130389Sle	return (s);
693130389Sle}
694137727Sle
695137727Sle/*
696137727Sle * Take a size in bytes and return a pointer to a string which represents the
697137727Sle * size best.  If lj is != 0, return left justified, otherwise in a fixed 10
698137727Sle * character field suitable for columnar printing.
699137727Sle *
700137727Sle * Note this uses a static string: it's only intended to be used immediately
701137727Sle * for printing.
702137727Sle */
703137727Sleconst char *
704137727Slegv_roughlength(off_t bytes, int lj)
705137727Sle{
706137727Sle	static char desc[16];
707137727Sle
708137727Sle	/* Gigabytes. */
709137727Sle	if (bytes > (off_t)MEGABYTE * 10000)
710137727Sle		snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB",
711137727Sle		    bytes / GIGABYTE);
712137727Sle
713137727Sle	/* Megabytes. */
714137727Sle	else if (bytes > KILOBYTE * 10000)
715137727Sle		snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB",
716137727Sle		    bytes / MEGABYTE);
717137727Sle
718137727Sle	/* Kilobytes. */
719137727Sle	else if (bytes > 10000)
720137727Sle		snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB",
721137727Sle		    bytes / KILOBYTE);
722137727Sle
723137727Sle	/* Bytes. */
724137727Sle	else
725137727Sle		snprintf(desc, sizeof(desc), lj ? "%jd  B" : "%10jd  B", bytes);
726137727Sle
727137727Sle	return (desc);
728137727Sle}
729