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