Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / lvm.c
blob508e94af0c0b50f2892dc2745b198a4c14efd114
1 /* lvm.c - module to read Logical Volumes. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2006,2007,2008,2009,2011 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/dl.h>
21 #include <grub/disk.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/misc.h>
25 #include <grub/lvm.h>
26 #include <grub/partition.h>
27 #include <grub/i18n.h>
29 #ifdef GRUB_UTIL
30 #include <grub/emu/misc.h>
31 #include <grub/emu/hostdisk.h>
32 #endif
34 GRUB_MOD_LICENSE ("GPLv3+");
37 /* Go the string STR and return the number after STR. *P will point
38 at the number. In case STR is not found, *P will be NULL and the
39 return value will be 0. */
40 static grub_uint64_t
41 grub_lvm_getvalue (char **p, const char *str)
43 *p = grub_strstr (*p, str);
44 if (! *p)
45 return 0;
46 *p += grub_strlen (str);
47 return grub_strtoull (*p, p, 10);
50 #if 0
51 static int
52 grub_lvm_checkvalue (char **p, char *str, char *tmpl)
54 int tmpllen = grub_strlen (tmpl);
55 *p = grub_strstr (*p, str);
56 if (! *p)
57 return 0;
58 *p += grub_strlen (str);
59 if (**p != '"')
60 return 0;
61 return (grub_memcmp (*p + 1, tmpl, tmpllen) == 0 && (*p)[tmpllen + 1] == '"');
63 #endif
65 static int
66 grub_lvm_check_flag (char *p, const char *str, const char *flag)
68 int len_str = grub_strlen (str), len_flag = grub_strlen (flag);
69 while (1)
71 char *q;
72 p = grub_strstr (p, str);
73 if (! p)
74 return 0;
75 p += len_str;
76 if (grub_memcmp (p, " = [", sizeof (" = [") - 1) != 0)
77 continue;
78 q = p + sizeof (" = [") - 1;
79 while (1)
81 while (grub_isspace (*q))
82 q++;
83 if (*q != '"')
84 return 0;
85 q++;
86 if (grub_memcmp (q, flag, len_flag) == 0 && q[len_flag] == '"')
87 return 1;
88 while (*q != '"')
89 q++;
90 q++;
91 if (*q == ']')
92 return 0;
93 q++;
98 static struct grub_diskfilter_vg *
99 grub_lvm_detect (grub_disk_t disk,
100 struct grub_diskfilter_pv_id *id,
101 grub_disk_addr_t *start_sector)
103 grub_err_t err;
104 grub_uint64_t mda_offset, mda_size;
105 char buf[GRUB_LVM_LABEL_SIZE];
106 char vg_id[GRUB_LVM_ID_STRLEN+1];
107 char pv_id[GRUB_LVM_ID_STRLEN+1];
108 char *metadatabuf, *p, *q, *vgname;
109 struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
110 struct grub_lvm_pv_header *pvh;
111 struct grub_lvm_disk_locn *dlocn;
112 struct grub_lvm_mda_header *mdah;
113 struct grub_lvm_raw_locn *rlocn;
114 unsigned int i, j, vgname_len;
115 struct grub_diskfilter_vg *vg;
116 struct grub_diskfilter_pv *pv;
118 /* Search for label. */
119 for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
121 err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
122 if (err)
123 goto fail;
125 if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
126 sizeof (lh->id)))
127 && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
128 sizeof (lh->type))))
129 break;
132 /* Return if we didn't find a label. */
133 if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
135 #ifdef GRUB_UTIL
136 grub_util_info ("no LVM signature found");
137 #endif
138 goto fail;
141 pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
143 for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
145 pv_id[j++] = pvh->pv_uuid[i];
146 if ((i != 1) && (i != 29) && (i % 4 == 1))
147 pv_id[j++] = '-';
149 pv_id[j] = '\0';
151 dlocn = pvh->disk_areas_xl;
153 dlocn++;
154 /* Is it possible to have multiple data/metadata areas? I haven't
155 seen devices that have it. */
156 if (dlocn->offset)
158 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
159 "we don't support multiple LVM data areas");
161 #ifdef GRUB_UTIL
162 grub_util_info ("we don't support multiple LVM data areas\n");
163 #endif
164 goto fail;
167 dlocn++;
168 mda_offset = grub_le_to_cpu64 (dlocn->offset);
169 mda_size = grub_le_to_cpu64 (dlocn->size);
171 /* It's possible to have multiple copies of metadata areas, we just use the
172 first one. */
174 /* Allocate buffer space for the circular worst-case scenario. */
175 metadatabuf = grub_malloc (2 * mda_size);
176 if (! metadatabuf)
177 goto fail;
179 err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf);
180 if (err)
181 goto fail2;
183 mdah = (struct grub_lvm_mda_header *) metadatabuf;
184 if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC,
185 sizeof (mdah->magic)))
186 || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION))
188 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
189 "unknown LVM metadata header");
190 #ifdef GRUB_UTIL
191 grub_util_info ("unknown LVM metadata header\n");
192 #endif
193 goto fail2;
196 rlocn = mdah->raw_locns;
197 if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) >
198 grub_le_to_cpu64 (mdah->size))
200 /* Metadata is circular. Copy the wrap in place. */
201 grub_memcpy (metadatabuf + mda_size,
202 metadatabuf + GRUB_LVM_MDA_HEADER_SIZE,
203 grub_le_to_cpu64 (rlocn->offset) +
204 grub_le_to_cpu64 (rlocn->size) -
205 grub_le_to_cpu64 (mdah->size));
207 p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
209 while (*q != ' ' && q < metadatabuf + mda_size)
210 q++;
212 if (q == metadatabuf + mda_size)
214 #ifdef GRUB_UTIL
215 grub_util_info ("error parsing metadata\n");
216 #endif
217 goto fail2;
220 vgname_len = q - p;
221 vgname = grub_malloc (vgname_len + 1);
222 if (!vgname)
223 goto fail2;
225 grub_memcpy (vgname, p, vgname_len);
226 vgname[vgname_len] = '\0';
228 p = grub_strstr (q, "id = \"");
229 if (p == NULL)
231 #ifdef GRUB_UTIL
232 grub_util_info ("couldn't find ID\n");
233 #endif
234 goto fail3;
236 p += sizeof ("id = \"") - 1;
237 grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN);
238 vg_id[GRUB_LVM_ID_STRLEN] = '\0';
240 vg = grub_diskfilter_get_vg_by_uuid (GRUB_LVM_ID_STRLEN, vg_id);
242 if (! vg)
244 /* First time we see this volume group. We've to create the
245 whole volume group structure. */
246 vg = grub_malloc (sizeof (*vg));
247 if (! vg)
248 goto fail3;
249 vg->name = vgname;
250 vg->uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
251 if (! vg->uuid)
252 goto fail3;
253 grub_memcpy (vg->uuid, vg_id, GRUB_LVM_ID_STRLEN);
254 vg->uuid_len = GRUB_LVM_ID_STRLEN;
256 vg->extent_size = grub_lvm_getvalue (&p, "extent_size = ");
257 if (p == NULL)
259 #ifdef GRUB_UTIL
260 grub_util_info ("unknown extent size\n");
261 #endif
262 goto fail4;
265 vg->lvs = NULL;
266 vg->pvs = NULL;
268 p = grub_strstr (p, "physical_volumes {");
269 if (p)
271 p += sizeof ("physical_volumes {") - 1;
273 /* Add all the pvs to the volume group. */
274 while (1)
276 int s;
277 while (grub_isspace (*p))
278 p++;
280 if (*p == '}')
281 break;
283 pv = grub_zalloc (sizeof (*pv));
284 q = p;
285 while (*q != ' ')
286 q++;
288 s = q - p;
289 pv->name = grub_malloc (s + 1);
290 grub_memcpy (pv->name, p, s);
291 pv->name[s] = '\0';
293 p = grub_strstr (p, "id = \"");
294 if (p == NULL)
295 goto pvs_fail;
296 p += sizeof("id = \"") - 1;
298 pv->id.uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
299 if (!pv->id.uuid)
300 goto pvs_fail;
301 grub_memcpy (pv->id.uuid, p, GRUB_LVM_ID_STRLEN);
302 pv->id.uuidlen = GRUB_LVM_ID_STRLEN;
304 pv->start_sector = grub_lvm_getvalue (&p, "pe_start = ");
305 if (p == NULL)
307 #ifdef GRUB_UTIL
308 grub_util_info ("unknown pe_start\n");
309 #endif
310 goto pvs_fail;
313 p = grub_strchr (p, '}');
314 if (p == NULL)
316 #ifdef GRUB_UTIL
317 grub_util_info ("error parsing pe_start\n");
318 #endif
319 goto pvs_fail;
321 p++;
323 pv->disk = NULL;
324 pv->next = vg->pvs;
325 vg->pvs = pv;
327 continue;
328 pvs_fail:
329 grub_free (pv->name);
330 grub_free (pv);
331 goto fail4;
335 p = grub_strstr (p, "logical_volumes");
336 if (p)
338 p += sizeof ("logical_volumes = ") - 1;
340 /* And add all the lvs to the volume group. */
341 while (1)
343 int s;
344 int skip_lv = 0;
345 struct grub_diskfilter_lv *lv;
346 struct grub_diskfilter_segment *seg;
347 int is_pvmove;
349 while (grub_isspace (*p))
350 p++;
352 if (*p == '}')
353 break;
355 lv = grub_zalloc (sizeof (*lv));
357 q = p;
358 while (*q != ' ')
359 q++;
361 s = q - p;
362 lv->name = grub_strndup (p, s);
363 if (!lv->name)
364 goto lvs_fail;
367 const char *iptr;
368 char *optr;
369 lv->fullname = grub_malloc (sizeof ("lvm/") - 1 + 2 * vgname_len
370 + 1 + 2 * s + 1);
371 if (!lv->fullname)
372 goto lvs_fail;
374 grub_memcpy (lv->fullname, "lvm/", sizeof ("lvm/") - 1);
375 optr = lv->fullname + sizeof ("lvm/") - 1;
376 for (iptr = vgname; iptr < vgname + vgname_len; iptr++)
378 *optr++ = *iptr;
379 if (*iptr == '-')
380 *optr++ = '-';
382 *optr++ = '-';
383 for (iptr = p; iptr < p + s; iptr++)
385 *optr++ = *iptr;
386 if (*iptr == '-')
387 *optr++ = '-';
389 *optr++ = 0;
392 lv->size = 0;
394 lv->visible = grub_lvm_check_flag (p, "status", "VISIBLE");
395 is_pvmove = grub_lvm_check_flag (p, "status", "PVMOVE");
397 lv->segment_count = grub_lvm_getvalue (&p, "segment_count = ");
398 if (p == NULL)
400 #ifdef GRUB_UTIL
401 grub_util_info ("unknown segment_count\n");
402 #endif
403 goto lvs_fail;
405 lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count);
406 seg = lv->segments;
408 for (i = 0; i < lv->segment_count; i++)
411 p = grub_strstr (p, "segment");
412 if (p == NULL)
414 #ifdef GRUB_UTIL
415 grub_util_info ("unknown segment\n");
416 #endif
417 goto lvs_segment_fail;
420 seg->start_extent = grub_lvm_getvalue (&p, "start_extent = ");
421 if (p == NULL)
423 #ifdef GRUB_UTIL
424 grub_util_info ("unknown start_extent\n");
425 #endif
426 goto lvs_segment_fail;
428 seg->extent_count = grub_lvm_getvalue (&p, "extent_count = ");
429 if (p == NULL)
431 #ifdef GRUB_UTIL
432 grub_util_info ("unknown extent_count\n");
433 #endif
434 goto lvs_segment_fail;
437 p = grub_strstr (p, "type = \"");
438 if (p == NULL)
439 goto lvs_segment_fail;
440 p += sizeof("type = \"") - 1;
442 lv->size += seg->extent_count * vg->extent_size;
444 if (grub_memcmp (p, "striped\"",
445 sizeof ("striped\"") - 1) == 0)
447 struct grub_diskfilter_node *stripe;
449 seg->type = GRUB_DISKFILTER_STRIPED;
450 seg->node_count = grub_lvm_getvalue (&p, "stripe_count = ");
451 if (p == NULL)
453 #ifdef GRUB_UTIL
454 grub_util_info ("unknown stripe_count\n");
455 #endif
456 goto lvs_segment_fail;
459 if (seg->node_count != 1)
460 seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
462 seg->nodes = grub_zalloc (sizeof (*stripe)
463 * seg->node_count);
464 stripe = seg->nodes;
466 p = grub_strstr (p, "stripes = [");
467 if (p == NULL)
469 #ifdef GRUB_UTIL
470 grub_util_info ("unknown stripes\n");
471 #endif
472 goto lvs_segment_fail2;
474 p += sizeof("stripes = [") - 1;
476 for (j = 0; j < seg->node_count; j++)
478 p = grub_strchr (p, '"');
479 if (p == NULL)
480 continue;
481 q = ++p;
482 while (*q != '"')
483 q++;
485 s = q - p;
487 stripe->name = grub_malloc (s + 1);
488 if (stripe->name == NULL)
489 goto lvs_segment_fail2;
491 grub_memcpy (stripe->name, p, s);
492 stripe->name[s] = '\0';
494 p = q + 1;
496 stripe->start = grub_lvm_getvalue (&p, ",")
497 * vg->extent_size;
498 if (p == NULL)
499 continue;
501 stripe++;
504 else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1)
505 == 0)
507 seg->type = GRUB_DISKFILTER_MIRROR;
508 seg->node_count = grub_lvm_getvalue (&p, "mirror_count = ");
509 if (p == NULL)
511 #ifdef GRUB_UTIL
512 grub_util_info ("unknown mirror_count\n");
513 #endif
514 goto lvs_segment_fail;
517 seg->nodes = grub_zalloc (sizeof (seg->nodes[0])
518 * seg->node_count);
520 p = grub_strstr (p, "mirrors = [");
521 if (p == NULL)
523 #ifdef GRUB_UTIL
524 grub_util_info ("unknown mirrors\n");
525 #endif
526 goto lvs_segment_fail2;
528 p += sizeof("mirrors = [") - 1;
530 for (j = 0; j < seg->node_count; j++)
532 char *lvname;
534 p = grub_strchr (p, '"');
535 if (p == NULL)
536 continue;
537 q = ++p;
538 while (*q != '"')
539 q++;
541 s = q - p;
543 lvname = grub_malloc (s + 1);
544 if (lvname == NULL)
545 goto lvs_segment_fail2;
547 grub_memcpy (lvname, p, s);
548 lvname[s] = '\0';
549 seg->nodes[j].name = lvname;
550 p = q + 1;
552 /* Only first (original) is ok with in progress pvmove. */
553 if (is_pvmove)
554 seg->node_count = 1;
556 else if (grub_memcmp (p, "raid", sizeof ("raid") - 1)
557 == 0 && (p[sizeof ("raid") - 1] >= '4'
558 && p[sizeof ("raid") - 1] <= '6')
559 && p[sizeof ("raidX") - 1] == '"')
561 switch (p[sizeof ("raid") - 1])
563 case '4':
564 seg->type = GRUB_DISKFILTER_RAID4;
565 seg->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC;
566 break;
567 case '5':
568 seg->type = GRUB_DISKFILTER_RAID5;
569 seg->layout = GRUB_RAID_LAYOUT_LEFT_SYMMETRIC;
570 break;
571 case '6':
572 seg->type = GRUB_DISKFILTER_RAID6;
573 seg->layout = (GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC
574 | GRUB_RAID_LAYOUT_MUL_FROM_POS);
575 break;
577 seg->node_count = grub_lvm_getvalue (&p, "device_count = ");
579 if (p == NULL)
581 #ifdef GRUB_UTIL
582 grub_util_info ("unknown device_count\n");
583 #endif
584 goto lvs_segment_fail;
587 seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
588 if (p == NULL)
590 #ifdef GRUB_UTIL
591 grub_util_info ("unknown stripe_size\n");
592 #endif
593 goto lvs_segment_fail;
597 seg->nodes = grub_zalloc (sizeof (seg->nodes[0])
598 * seg->node_count);
600 p = grub_strstr (p, "raids = [");
601 if (p == NULL)
603 #ifdef GRUB_UTIL
604 grub_util_info ("unknown mirrors\n");
605 #endif
606 goto lvs_segment_fail2;
608 p += sizeof("raids = [") - 1;
610 for (j = 0; j < seg->node_count; j++)
612 char *lvname;
614 p = grub_strchr (p, '"');
615 p = p ? grub_strchr (p + 1, '"') : 0;
616 p = p ? grub_strchr (p + 1, '"') : 0;
617 if (p == NULL)
618 continue;
619 q = ++p;
620 while (*q != '"')
621 q++;
623 s = q - p;
625 lvname = grub_malloc (s + 1);
626 if (lvname == NULL)
627 goto lvs_segment_fail2;
629 grub_memcpy (lvname, p, s);
630 lvname[s] = '\0';
631 seg->nodes[j].name = lvname;
632 p = q + 1;
634 if (seg->type == GRUB_DISKFILTER_RAID4)
636 char *tmp;
637 tmp = seg->nodes[0].name;
638 grub_memmove (seg->nodes, seg->nodes + 1,
639 sizeof (seg->nodes[0])
640 * (seg->node_count - 1));
641 seg->nodes[seg->node_count - 1].name = tmp;
644 else
646 #ifdef GRUB_UTIL
647 char *p2;
648 p2 = grub_strchr (p, '"');
649 if (p2)
650 *p2 = 0;
651 grub_util_info ("unknown LVM type %s\n", p);
652 if (p2)
653 *p2 ='"';
654 #endif
655 /* Found a non-supported type, give up and move on. */
656 skip_lv = 1;
657 break;
660 seg++;
662 continue;
663 lvs_segment_fail2:
664 grub_free (seg->nodes);
665 lvs_segment_fail:
666 goto fail4;
669 if (p != NULL)
670 p = grub_strchr (p, '}');
671 if (p == NULL)
672 goto lvs_fail;
673 p += 3;
675 if (skip_lv)
677 grub_free (lv->name);
678 grub_free (lv);
679 continue;
682 lv->vg = vg;
683 lv->next = vg->lvs;
684 vg->lvs = lv;
686 continue;
687 lvs_fail:
688 grub_free (lv->name);
689 grub_free (lv);
690 goto fail4;
694 /* Match lvs. */
696 struct grub_diskfilter_lv *lv1;
697 struct grub_diskfilter_lv *lv2;
698 for (lv1 = vg->lvs; lv1; lv1 = lv1->next)
699 for (i = 0; i < lv1->segment_count; i++)
700 for (j = 0; j < lv1->segments[i].node_count; j++)
702 if (vg->pvs)
703 for (pv = vg->pvs; pv; pv = pv->next)
705 if (! grub_strcmp (pv->name,
706 lv1->segments[i].nodes[j].name))
708 lv1->segments[i].nodes[j].pv = pv;
709 break;
712 if (lv1->segments[i].nodes[j].pv == NULL)
713 for (lv2 = vg->lvs; lv2; lv2 = lv2->next)
714 if (grub_strcmp (lv2->name,
715 lv1->segments[i].nodes[j].name) == 0)
716 lv1->segments[i].nodes[j].lv = lv2;
720 if (grub_diskfilter_vg_register (vg))
721 goto fail4;
723 else
725 grub_free (vgname);
728 id->uuid = grub_malloc (GRUB_LVM_ID_STRLEN);
729 if (!id->uuid)
730 goto fail4;
731 grub_memcpy (id->uuid, pv_id, GRUB_LVM_ID_STRLEN);
732 id->uuidlen = GRUB_LVM_ID_STRLEN;
733 grub_free (metadatabuf);
734 *start_sector = -1;
735 return vg;
737 /* Failure path. */
738 fail4:
739 grub_free (vg);
740 fail3:
741 grub_free (vgname);
743 fail2:
744 grub_free (metadatabuf);
745 fail:
746 return NULL;
751 static struct grub_diskfilter grub_lvm_dev = {
752 .name = "lvm",
753 .detect = grub_lvm_detect,
754 .next = 0
757 GRUB_MOD_INIT (lvm)
759 grub_diskfilter_register_back (&grub_lvm_dev);
762 GRUB_MOD_FINI (lvm)
764 grub_diskfilter_unregister (&grub_lvm_dev);