Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / ldm.c
blob8075f2a9eeed827d12b05fd721c0bea6340553f6
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007,2008,2009,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/dl.h>
20 #include <grub/disk.h>
21 #include <grub/mm.h>
22 #include <grub/err.h>
23 #include <grub/misc.h>
24 #include <grub/diskfilter.h>
25 #include <grub/msdos_partition.h>
26 #include <grub/gpt_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+");
36 #define LDM_GUID_STRLEN 64
37 #define LDM_NAME_STRLEN 32
39 typedef grub_uint8_t *grub_ldm_id_t;
41 enum { STRIPE = 1, SPANNED = 2, RAID5 = 3 };
43 #define LDM_LABEL_SECTOR 6
44 struct grub_ldm_vblk {
45 char magic[4];
46 grub_uint8_t unused1[12];
47 grub_uint16_t update_status;
48 grub_uint8_t flags;
49 grub_uint8_t type;
50 grub_uint32_t unused2;
51 grub_uint8_t dynamic[104];
52 } GRUB_PACKED;
53 #define LDM_VBLK_MAGIC "VBLK"
55 enum
57 STATUS_CONSISTENT = 0,
58 STATUS_STILL_ACTIVE = 1,
59 STATUS_NOT_ACTIVE_YET = 2
62 enum
64 ENTRY_COMPONENT = 0x32,
65 ENTRY_PARTITION = 0x33,
66 ENTRY_DISK = 0x34,
67 ENTRY_VOLUME = 0x51,
70 struct grub_ldm_label
72 char magic[8];
73 grub_uint32_t unused1;
74 grub_uint16_t ver_major;
75 grub_uint16_t ver_minor;
76 grub_uint8_t unused2[32];
77 char disk_guid[LDM_GUID_STRLEN];
78 char host_guid[LDM_GUID_STRLEN];
79 char group_guid[LDM_GUID_STRLEN];
80 char group_name[LDM_NAME_STRLEN];
81 grub_uint8_t unused3[11];
82 grub_uint64_t pv_start;
83 grub_uint64_t pv_size;
84 grub_uint64_t config_start;
85 grub_uint64_t config_size;
86 } GRUB_PACKED;
89 #define LDM_MAGIC "PRIVHEAD"
93 static inline grub_uint64_t
94 read_int (grub_uint8_t *in, grub_size_t s)
96 grub_uint8_t *ptr2;
97 grub_uint64_t ret;
98 ret = 0;
99 for (ptr2 = in; ptr2 < in + s; ptr2++)
101 ret <<= 8;
102 ret |= *ptr2;
104 return ret;
107 static int
108 check_ldm_partition (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p, void *data)
110 int *has_ldm = data;
112 if (p->number >= 4)
113 return 1;
114 if (p->msdostype == GRUB_PC_PARTITION_TYPE_LDM)
116 *has_ldm = 1;
117 return 1;
119 return 0;
122 static int
123 msdos_has_ldm_partition (grub_disk_t dsk)
125 grub_err_t err;
126 int has_ldm = 0;
128 err = grub_partition_msdos_iterate (dsk, check_ldm_partition, &has_ldm);
129 if (err)
131 grub_errno = GRUB_ERR_NONE;
132 return 0;
135 return has_ldm;
138 static const grub_gpt_part_type_t ldm_type = GRUB_GPT_PARTITION_TYPE_LDM;
140 /* Helper for gpt_ldm_sector. */
141 static int
142 gpt_ldm_sector_iter (grub_disk_t disk, const grub_partition_t p, void *data)
144 grub_disk_addr_t *sector = data;
145 struct grub_gpt_partentry gptdata;
146 grub_partition_t p2;
148 p2 = disk->partition;
149 disk->partition = p->parent;
150 if (grub_disk_read (disk, p->offset, p->index,
151 sizeof (gptdata), &gptdata))
153 disk->partition = p2;
154 return 0;
156 disk->partition = p2;
158 if (! grub_memcmp (&gptdata.type, &ldm_type, 16))
160 *sector = p->start + p->len - 1;
161 return 1;
163 return 0;
166 static grub_disk_addr_t
167 gpt_ldm_sector (grub_disk_t dsk)
169 grub_disk_addr_t sector = 0;
170 grub_err_t err;
172 err = grub_gpt_partition_map_iterate (dsk, gpt_ldm_sector_iter, &sector);
173 if (err)
175 grub_errno = GRUB_ERR_NONE;
176 return 0;
178 return sector;
181 static struct grub_diskfilter_vg *
182 make_vg (grub_disk_t disk,
183 const struct grub_ldm_label *label)
185 grub_disk_addr_t startsec, endsec, cursec;
186 struct grub_diskfilter_vg *vg;
187 grub_err_t err;
189 /* First time we see this volume group. We've to create the
190 whole volume group structure. */
191 vg = grub_malloc (sizeof (*vg));
192 if (! vg)
193 return NULL;
194 vg->extent_size = 1;
195 vg->name = grub_malloc (LDM_NAME_STRLEN + 1);
196 vg->uuid = grub_malloc (LDM_GUID_STRLEN + 1);
197 if (! vg->uuid || !vg->name)
199 grub_free (vg->uuid);
200 grub_free (vg->name);
201 return NULL;
203 grub_memcpy (vg->uuid, label->group_guid, LDM_GUID_STRLEN);
204 grub_memcpy (vg->name, label->group_name, LDM_NAME_STRLEN);
205 vg->name[LDM_NAME_STRLEN] = 0;
206 vg->uuid[LDM_GUID_STRLEN] = 0;
207 vg->uuid_len = grub_strlen (vg->uuid);
209 vg->lvs = NULL;
210 vg->pvs = NULL;
212 startsec = grub_be_to_cpu64 (label->config_start);
213 endsec = startsec + grub_be_to_cpu64 (label->config_size);
215 /* First find disks. */
216 for (cursec = startsec + 0x12; cursec < endsec; cursec++)
218 struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
219 / sizeof (struct grub_ldm_vblk)];
220 unsigned i;
221 err = grub_disk_read (disk, cursec, 0,
222 sizeof(vblk), &vblk);
223 if (err)
224 goto fail2;
226 for (i = 0; i < ARRAY_SIZE (vblk); i++)
228 struct grub_diskfilter_pv *pv;
229 grub_uint8_t *ptr;
230 if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
231 sizeof (vblk[i].magic)) != 0)
232 continue;
233 if (grub_be_to_cpu16 (vblk[i].update_status)
234 != STATUS_CONSISTENT
235 && grub_be_to_cpu16 (vblk[i].update_status)
236 != STATUS_STILL_ACTIVE)
237 continue;
238 if (vblk[i].type != ENTRY_DISK)
239 continue;
240 pv = grub_zalloc (sizeof (*pv));
241 if (!pv)
242 goto fail2;
244 pv->disk = 0;
245 ptr = vblk[i].dynamic;
246 if (ptr + *ptr + 1 >= vblk[i].dynamic
247 + sizeof (vblk[i].dynamic))
249 grub_free (pv);
250 goto fail2;
252 pv->internal_id = grub_malloc (ptr[0] + 2);
253 if (!pv->internal_id)
255 grub_free (pv);
256 goto fail2;
258 grub_memcpy (pv->internal_id, ptr, (grub_size_t) ptr[0] + 1);
259 pv->internal_id[(grub_size_t) ptr[0] + 1] = 0;
261 ptr += *ptr + 1;
262 if (ptr + *ptr + 1 >= vblk[i].dynamic
263 + sizeof (vblk[i].dynamic))
265 grub_free (pv);
266 goto fail2;
268 /* ptr = name. */
269 ptr += *ptr + 1;
270 if (ptr + *ptr + 1
271 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
273 grub_free (pv);
274 goto fail2;
276 pv->id.uuidlen = *ptr;
277 pv->id.uuid = grub_malloc (pv->id.uuidlen + 1);
278 grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen);
279 pv->id.uuid[pv->id.uuidlen] = 0;
281 pv->next = vg->pvs;
282 vg->pvs = pv;
286 /* Then find LVs. */
287 for (cursec = startsec + 0x12; cursec < endsec; cursec++)
289 struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
290 / sizeof (struct grub_ldm_vblk)];
291 unsigned i;
292 err = grub_disk_read (disk, cursec, 0,
293 sizeof(vblk), &vblk);
294 if (err)
295 goto fail2;
297 for (i = 0; i < ARRAY_SIZE (vblk); i++)
299 struct grub_diskfilter_lv *lv;
300 grub_uint8_t *ptr;
301 if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
302 sizeof (vblk[i].magic)) != 0)
303 continue;
304 if (grub_be_to_cpu16 (vblk[i].update_status)
305 != STATUS_CONSISTENT
306 && grub_be_to_cpu16 (vblk[i].update_status)
307 != STATUS_STILL_ACTIVE)
308 continue;
309 if (vblk[i].type != ENTRY_VOLUME)
310 continue;
311 lv = grub_zalloc (sizeof (*lv));
312 if (!lv)
313 goto fail2;
315 lv->vg = vg;
316 lv->segment_count = 1;
317 lv->segment_alloc = 1;
318 lv->visible = 1;
319 lv->segments = grub_zalloc (sizeof (*lv->segments));
320 if (!lv->segments)
321 goto fail2;
322 lv->segments->start_extent = 0;
323 lv->segments->type = GRUB_DISKFILTER_MIRROR;
324 lv->segments->node_count = 0;
325 lv->segments->node_alloc = 8;
326 lv->segments->nodes = grub_zalloc (sizeof (*lv->segments->nodes)
327 * lv->segments->node_alloc);
328 if (!lv->segments->nodes)
329 goto fail2;
330 ptr = vblk[i].dynamic;
331 if (ptr + *ptr + 1 >= vblk[i].dynamic
332 + sizeof (vblk[i].dynamic))
334 grub_free (lv);
335 goto fail2;
337 lv->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2);
338 if (!lv->internal_id)
340 grub_free (lv);
341 goto fail2;
343 grub_memcpy (lv->internal_id, ptr, ptr[0] + 1);
344 lv->internal_id[ptr[0] + 1] = 0;
346 ptr += *ptr + 1;
347 if (ptr + *ptr + 1 >= vblk[i].dynamic
348 + sizeof (vblk[i].dynamic))
350 grub_free (lv);
351 goto fail2;
353 lv->name = grub_malloc (*ptr + 1);
354 if (!lv->name)
356 grub_free (lv->internal_id);
357 grub_free (lv);
358 goto fail2;
360 grub_memcpy (lv->name, ptr + 1, *ptr);
361 lv->name[*ptr] = 0;
362 lv->fullname = grub_xasprintf ("ldm/%s/%s",
363 vg->uuid, lv->name);
364 if (!lv->fullname)
366 grub_free (lv->internal_id);
367 grub_free (lv->name);
368 grub_free (lv);
369 goto fail2;
371 ptr += *ptr + 1;
372 if (ptr + *ptr + 1
373 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
375 grub_free (lv->internal_id);
376 grub_free (lv->name);
377 grub_free (lv);
378 goto fail2;
380 /* ptr = volume type. */
381 ptr += *ptr + 1;
382 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
384 grub_free (lv->internal_id);
385 grub_free (lv->name);
386 grub_free (lv);
387 goto fail2;
389 /* ptr = flags. */
390 ptr += *ptr + 1;
391 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
393 grub_free (lv->internal_id);
394 grub_free (lv->name);
395 grub_free (lv);
396 goto fail2;
399 /* Skip state, type, unknown, volume number, zeros, flags. */
400 ptr += 14 + 1 + 1 + 1 + 3 + 1;
401 /* ptr = number of children. */
402 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
404 grub_free (lv->internal_id);
405 grub_free (lv->name);
406 grub_free (lv);
407 goto fail2;
409 ptr += *ptr + 1;
410 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
412 grub_free (lv->internal_id);
413 grub_free (lv->name);
414 grub_free (lv);
415 goto fail2;
418 /* Skip 2 more fields. */
419 ptr += 8 + 8;
420 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
421 || ptr + *ptr + 1>= vblk[i].dynamic
422 + sizeof (vblk[i].dynamic))
424 grub_free (lv->internal_id);
425 grub_free (lv->name);
426 grub_free (lv);
427 goto fail2;
429 lv->size = read_int (ptr + 1, *ptr);
430 lv->segments->extent_count = lv->size;
432 lv->next = vg->lvs;
433 vg->lvs = lv;
437 /* Now the components. */
438 for (cursec = startsec + 0x12; cursec < endsec; cursec++)
440 struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
441 / sizeof (struct grub_ldm_vblk)];
442 unsigned i;
443 err = grub_disk_read (disk, cursec, 0,
444 sizeof(vblk), &vblk);
445 if (err)
446 goto fail2;
448 for (i = 0; i < ARRAY_SIZE (vblk); i++)
450 struct grub_diskfilter_lv *comp;
451 struct grub_diskfilter_lv *lv;
452 grub_uint8_t type;
454 grub_uint8_t *ptr;
455 if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
456 sizeof (vblk[i].magic)) != 0)
457 continue;
458 if (grub_be_to_cpu16 (vblk[i].update_status)
459 != STATUS_CONSISTENT
460 && grub_be_to_cpu16 (vblk[i].update_status)
461 != STATUS_STILL_ACTIVE)
462 continue;
463 if (vblk[i].type != ENTRY_COMPONENT)
464 continue;
465 comp = grub_zalloc (sizeof (*comp));
466 if (!comp)
467 goto fail2;
468 comp->visible = 0;
469 comp->name = 0;
470 comp->fullname = 0;
472 ptr = vblk[i].dynamic;
473 if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
475 goto fail2;
477 comp->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2);
478 if (!comp->internal_id)
480 grub_free (comp);
481 goto fail2;
483 grub_memcpy (comp->internal_id, ptr, ptr[0] + 1);
484 comp->internal_id[ptr[0] + 1] = 0;
486 ptr += *ptr + 1;
487 if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
489 grub_free (comp->internal_id);
490 grub_free (comp);
491 goto fail2;
493 /* ptr = name. */
494 ptr += *ptr + 1;
495 if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
497 grub_free (comp->internal_id);
498 grub_free (comp);
499 goto fail2;
501 /* ptr = state. */
502 ptr += *ptr + 1;
503 type = *ptr++;
504 /* skip zeros. */
505 ptr += 4;
506 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
508 grub_free (comp->internal_id);
509 grub_free (comp);
510 goto fail2;
513 /* ptr = number of children. */
514 ptr += *ptr + 1;
515 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
517 grub_free (comp->internal_id);
518 grub_free (comp);
519 goto fail2;
521 ptr += 8 + 8;
522 if (ptr + *ptr + 1 >= vblk[i].dynamic
523 + sizeof (vblk[i].dynamic))
525 grub_free (comp->internal_id);
526 grub_free (comp);
527 goto fail2;
529 for (lv = vg->lvs; lv; lv = lv->next)
531 if (lv->internal_id[0] == ptr[0]
532 && grub_memcmp (lv->internal_id + 1, ptr + 1, ptr[0]) == 0)
533 break;
535 if (!lv)
537 grub_free (comp->internal_id);
538 grub_free (comp);
539 continue;
541 comp->size = lv->size;
542 if (type == SPANNED)
544 comp->segment_alloc = 8;
545 comp->segment_count = 0;
546 comp->segments = grub_malloc (sizeof (*comp->segments)
547 * comp->segment_alloc);
548 if (!comp->segments)
549 goto fail2;
551 else
553 comp->segment_alloc = 1;
554 comp->segment_count = 1;
555 comp->segments = grub_malloc (sizeof (*comp->segments));
556 if (!comp->segments)
557 goto fail2;
558 comp->segments->start_extent = 0;
559 comp->segments->extent_count = lv->size;
560 comp->segments->layout = 0;
561 if (type == STRIPE)
562 comp->segments->type = GRUB_DISKFILTER_STRIPED;
563 else if (type == RAID5)
565 comp->segments->type = GRUB_DISKFILTER_RAID5;
566 comp->segments->layout = GRUB_RAID_LAYOUT_SYMMETRIC_MASK;
568 else
569 goto fail2;
570 ptr += *ptr + 1;
571 ptr++;
572 if (!(vblk[i].flags & 0x10))
573 goto fail2;
574 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
575 || ptr + *ptr + 1 >= vblk[i].dynamic
576 + sizeof (vblk[i].dynamic))
578 grub_free (comp->internal_id);
579 grub_free (comp);
580 goto fail2;
582 comp->segments->stripe_size = read_int (ptr + 1, *ptr);
583 ptr += *ptr + 1;
584 if (ptr + *ptr + 1 >= vblk[i].dynamic
585 + sizeof (vblk[i].dynamic))
587 grub_free (comp->internal_id);
588 grub_free (comp);
589 goto fail2;
591 comp->segments->node_count = read_int (ptr + 1, *ptr);
592 comp->segments->node_alloc = comp->segments->node_count;
593 comp->segments->nodes = grub_zalloc (sizeof (*comp->segments->nodes)
594 * comp->segments->node_alloc);
595 if (!lv->segments->nodes)
596 goto fail2;
599 if (lv->segments->node_alloc == lv->segments->node_count)
601 void *t;
602 lv->segments->node_alloc *= 2;
603 t = grub_realloc (lv->segments->nodes,
604 sizeof (*lv->segments->nodes)
605 * lv->segments->node_alloc);
606 if (!t)
607 goto fail2;
608 lv->segments->nodes = t;
610 lv->segments->nodes[lv->segments->node_count].pv = 0;
611 lv->segments->nodes[lv->segments->node_count].start = 0;
612 lv->segments->nodes[lv->segments->node_count++].lv = comp;
613 comp->next = vg->lvs;
614 vg->lvs = comp;
617 /* Partitions. */
618 for (cursec = startsec + 0x12; cursec < endsec; cursec++)
620 struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
621 / sizeof (struct grub_ldm_vblk)];
622 unsigned i;
623 err = grub_disk_read (disk, cursec, 0,
624 sizeof(vblk), &vblk);
625 if (err)
626 goto fail2;
628 for (i = 0; i < ARRAY_SIZE (vblk); i++)
630 struct grub_diskfilter_lv *comp;
631 struct grub_diskfilter_node part;
632 grub_disk_addr_t start, size;
634 grub_uint8_t *ptr;
635 part.name = 0;
636 if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC,
637 sizeof (vblk[i].magic)) != 0)
638 continue;
639 if (grub_be_to_cpu16 (vblk[i].update_status)
640 != STATUS_CONSISTENT
641 && grub_be_to_cpu16 (vblk[i].update_status)
642 != STATUS_STILL_ACTIVE)
643 continue;
644 if (vblk[i].type != ENTRY_PARTITION)
645 continue;
646 part.lv = 0;
647 part.pv = 0;
649 ptr = vblk[i].dynamic;
650 if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
652 goto fail2;
654 /* ID */
655 ptr += *ptr + 1;
656 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
658 goto fail2;
660 /* ptr = name. */
661 ptr += *ptr + 1;
662 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
664 goto fail2;
667 /* skip zeros and logcommit id. */
668 ptr += 4 + 8;
669 if (ptr + 16 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
671 goto fail2;
673 part.start = read_int (ptr, 8);
674 start = read_int (ptr + 8, 8);
675 ptr += 16;
676 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
677 || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
679 goto fail2;
681 size = read_int (ptr + 1, *ptr);
682 ptr += *ptr + 1;
683 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
684 || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
686 goto fail2;
689 for (comp = vg->lvs; comp; comp = comp->next)
690 if (comp->internal_id[0] == ptr[0]
691 && grub_memcmp (ptr + 1, comp->internal_id + 1,
692 comp->internal_id[0]) == 0)
693 goto out;
694 continue;
695 out:
696 if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)
697 || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic))
699 goto fail2;
701 ptr += *ptr + 1;
702 struct grub_diskfilter_pv *pv;
703 for (pv = vg->pvs; pv; pv = pv->next)
704 if (pv->internal_id[0] == ptr[0]
705 && grub_memcmp (pv->internal_id + 1, ptr + 1, ptr[0]) == 0)
706 part.pv = pv;
708 if (comp->segment_alloc == 1)
710 unsigned node_index;
711 ptr += *ptr + 1;
712 if (ptr + *ptr + 1 >= vblk[i].dynamic
713 + sizeof (vblk[i].dynamic))
715 goto fail2;
717 node_index = read_int (ptr + 1, *ptr);
718 if (node_index < comp->segments->node_count)
719 comp->segments->nodes[node_index] = part;
721 else
723 if (comp->segment_alloc == comp->segment_count)
725 void *t;
726 comp->segment_alloc *= 2;
727 t = grub_realloc (comp->segments,
728 comp->segment_alloc
729 * sizeof (*comp->segments));
730 if (!t)
731 goto fail2;
732 comp->segments = t;
734 comp->segments[comp->segment_count].start_extent = start;
735 comp->segments[comp->segment_count].extent_count = size;
736 comp->segments[comp->segment_count].type = GRUB_DISKFILTER_STRIPED;
737 comp->segments[comp->segment_count].node_count = 1;
738 comp->segments[comp->segment_count].node_alloc = 1;
739 comp->segments[comp->segment_count].nodes
740 = grub_malloc (sizeof (*comp->segments[comp->segment_count].nodes));
741 if (!comp->segments[comp->segment_count].nodes)
742 goto fail2;
743 comp->segments[comp->segment_count].nodes[0] = part;
744 comp->segment_count++;
748 if (grub_diskfilter_vg_register (vg))
749 goto fail2;
750 return vg;
751 fail2:
753 struct grub_diskfilter_lv *lv, *next_lv;
754 struct grub_diskfilter_pv *pv, *next_pv;
755 for (lv = vg->lvs; lv; lv = next_lv)
757 unsigned i;
758 for (i = 0; i < lv->segment_count; i++)
759 grub_free (lv->segments[i].nodes);
761 next_lv = lv->next;
762 grub_free (lv->segments);
763 grub_free (lv->internal_id);
764 grub_free (lv->name);
765 grub_free (lv->fullname);
766 grub_free (lv);
768 for (pv = vg->pvs; pv; pv = next_pv)
770 next_pv = pv->next;
771 grub_free (pv->id.uuid);
772 grub_free (pv);
775 grub_free (vg->uuid);
776 grub_free (vg);
777 return NULL;
780 static struct grub_diskfilter_vg *
781 grub_ldm_detect (grub_disk_t disk,
782 struct grub_diskfilter_pv_id *id,
783 grub_disk_addr_t *start_sector)
785 grub_err_t err;
786 struct grub_ldm_label label;
787 struct grub_diskfilter_vg *vg;
789 #ifdef GRUB_UTIL
790 grub_util_info ("scanning %s for LDM", disk->name);
791 #endif
794 int i;
795 int has_ldm = msdos_has_ldm_partition (disk);
796 for (i = 0; i < 3; i++)
798 grub_disk_addr_t sector = LDM_LABEL_SECTOR;
799 switch (i)
801 case 0:
802 if (!has_ldm)
803 continue;
804 sector = LDM_LABEL_SECTOR;
805 break;
806 case 1:
807 /* LDM is never inside a partition. */
808 if (!has_ldm || disk->partition)
809 continue;
810 sector = grub_disk_get_size (disk);
811 if (sector == GRUB_DISK_SIZE_UNKNOWN)
812 continue;
813 sector--;
814 break;
815 /* FIXME: try the third copy. */
816 case 2:
817 sector = gpt_ldm_sector (disk);
818 if (!sector)
819 continue;
820 break;
822 err = grub_disk_read (disk, sector, 0,
823 sizeof(label), &label);
824 if (err)
825 return NULL;
826 if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0
827 && grub_be_to_cpu16 (label.ver_major) == 0x02
828 && grub_be_to_cpu16 (label.ver_minor) >= 0x0b
829 && grub_be_to_cpu16 (label.ver_minor) <= 0x0c)
830 break;
833 /* Return if we didn't find a label. */
834 if (i == 3)
836 #ifdef GRUB_UTIL
837 grub_util_info ("no LDM signature found");
838 #endif
839 return NULL;
843 id->uuid = grub_malloc (LDM_GUID_STRLEN + 1);
844 if (!id->uuid)
845 return NULL;
846 grub_memcpy (id->uuid, label.disk_guid, LDM_GUID_STRLEN);
847 id->uuid[LDM_GUID_STRLEN] = 0;
848 id->uuidlen = grub_strlen ((char *) id->uuid);
849 *start_sector = grub_be_to_cpu64 (label.pv_start);
852 grub_size_t s;
853 for (s = 0; s < LDM_GUID_STRLEN && label.group_guid[s]; s++);
854 vg = grub_diskfilter_get_vg_by_uuid (s, label.group_guid);
855 if (! vg)
856 vg = make_vg (disk, &label);
859 if (!vg)
861 grub_free (id->uuid);
862 return NULL;
864 return vg;
867 #ifdef GRUB_UTIL
869 char *
870 grub_util_get_ldm (grub_disk_t disk, grub_disk_addr_t start)
872 struct grub_diskfilter_pv *pv = NULL;
873 struct grub_diskfilter_vg *vg = NULL;
874 struct grub_diskfilter_lv *res = 0, *lv, *res_lv = 0;
876 pv = grub_diskfilter_get_pv_from_disk (disk, &vg);
878 if (!pv)
879 return NULL;
881 for (lv = vg->lvs; lv; lv = lv->next)
882 if (lv->segment_count == 1 && lv->segments->node_count == 1
883 && lv->segments->type == GRUB_DISKFILTER_STRIPED
884 && lv->segments->nodes->pv == pv
885 && lv->segments->nodes->start + pv->start_sector == start)
887 res_lv = lv;
888 break;
890 if (!res_lv)
891 return NULL;
892 for (lv = vg->lvs; lv; lv = lv->next)
893 if (lv->segment_count == 1 && lv->segments->node_count == 1
894 && lv->segments->type == GRUB_DISKFILTER_MIRROR
895 && lv->segments->nodes->lv == res_lv)
897 res = lv;
898 break;
900 if (res && res->fullname)
901 return grub_strdup (res->fullname);
902 return NULL;
906 grub_util_is_ldm (grub_disk_t disk)
908 int i;
909 int has_ldm = msdos_has_ldm_partition (disk);
910 for (i = 0; i < 3; i++)
912 grub_disk_addr_t sector = LDM_LABEL_SECTOR;
913 grub_err_t err;
914 struct grub_ldm_label label;
916 switch (i)
918 case 0:
919 if (!has_ldm)
920 continue;
921 sector = LDM_LABEL_SECTOR;
922 break;
923 case 1:
924 /* LDM is never inside a partition. */
925 if (!has_ldm || disk->partition)
926 continue;
927 sector = grub_disk_get_size (disk);
928 if (sector == GRUB_DISK_SIZE_UNKNOWN)
929 continue;
930 sector--;
931 break;
932 /* FIXME: try the third copy. */
933 case 2:
934 sector = gpt_ldm_sector (disk);
935 if (!sector)
936 continue;
937 break;
939 err = grub_disk_read (disk, sector, 0, sizeof(label), &label);
940 if (err)
942 grub_errno = GRUB_ERR_NONE;
943 return 0;
945 /* This check is more relaxed on purpose. */
946 if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0)
947 return 1;
950 return 0;
953 grub_err_t
954 grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors,
955 unsigned int max_nsectors,
956 grub_embed_type_t embed_type,
957 grub_disk_addr_t **sectors)
959 struct grub_diskfilter_pv *pv = NULL;
960 struct grub_diskfilter_vg *vg;
961 struct grub_diskfilter_lv *lv;
962 unsigned i;
964 if (embed_type != GRUB_EMBED_PCBIOS)
965 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
966 "LDM curently supports only PC-BIOS embedding");
967 if (disk->partition)
968 return grub_error (GRUB_ERR_BUG, "disk isn't LDM");
969 pv = grub_diskfilter_get_pv_from_disk (disk, &vg);
970 if (!pv)
971 return grub_error (GRUB_ERR_BUG, "disk isn't LDM");
972 for (lv = vg->lvs; lv; lv = lv->next)
974 struct grub_diskfilter_lv *comp;
976 if (!lv->visible || !lv->fullname)
977 continue;
979 if (lv->segment_count != 1)
980 continue;
981 if (lv->segments->type != GRUB_DISKFILTER_MIRROR
982 || lv->segments->node_count != 1
983 || lv->segments->start_extent != 0
984 || lv->segments->extent_count != lv->size)
985 continue;
987 comp = lv->segments->nodes->lv;
988 if (!comp)
989 continue;
991 if (comp->segment_count != 1 || comp->size != lv->size)
992 continue;
993 if (comp->segments->type != GRUB_DISKFILTER_STRIPED
994 || comp->segments->node_count != 1
995 || comp->segments->start_extent != 0
996 || comp->segments->extent_count != lv->size)
997 continue;
999 /* How to implement proper check is to be discussed. */
1000 #if 1
1001 if (1)
1002 continue;
1003 #else
1004 if (grub_strcmp (lv->name, "Volume5") != 0)
1005 continue;
1006 #endif
1007 if (lv->size < *nsectors)
1008 return grub_error (GRUB_ERR_OUT_OF_RANGE,
1009 /* TRANSLATORS: it's a partition for embedding,
1010 not a partition embed into something. GRUB
1011 install tools put core.img into a place
1012 usable for bootloaders (called generically
1013 "embedding zone") and this operation is
1014 called "embedding". */
1015 N_("your LDM Embedding Partition is too small;"
1016 " embedding won't be possible"));
1017 *nsectors = lv->size;
1018 if (*nsectors > max_nsectors)
1019 *nsectors = max_nsectors;
1020 *sectors = grub_malloc (*nsectors * sizeof (**sectors));
1021 if (!*sectors)
1022 return grub_errno;
1023 for (i = 0; i < *nsectors; i++)
1024 (*sectors)[i] = (lv->segments->nodes->start
1025 + comp->segments->nodes->start
1026 + comp->segments->nodes->pv->start_sector + i);
1027 return GRUB_ERR_NONE;
1030 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
1031 /* TRANSLATORS: it's a partition for embedding,
1032 not a partition embed into something. */
1033 N_("this LDM has no Embedding Partition;"
1034 " embedding won't be possible"));
1036 #endif
1038 static struct grub_diskfilter grub_ldm_dev = {
1039 .name = "ldm",
1040 .detect = grub_ldm_detect,
1041 .next = 0
1044 GRUB_MOD_INIT (ldm)
1046 grub_diskfilter_register_back (&grub_ldm_dev);
1049 GRUB_MOD_FINI (ldm)
1051 grub_diskfilter_unregister (&grub_ldm_dev);