source: grub-pc/trunk/fuentes/.pc/probe-delimiter.patch/grub-core/disk/diskfilter.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 28.8 KB
Line 
1/* diskfilter.c - module to read RAID arrays.  */
2/*
3 *  GRUB  --  GRand Unified Bootloader
4 *  Copyright (C) 2006,2007,2008,2009,2010  Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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/diskfilter.h>
26#include <grub/partition.h>
27#ifdef GRUB_UTIL
28#include <grub/i18n.h>
29#include <grub/util/misc.h>
30#endif
31
32GRUB_MOD_LICENSE ("GPLv3+");
33
34/* Linked list of DISKFILTER arrays. */
35static struct grub_diskfilter_vg *array_list;
36grub_raid5_recover_func_t grub_raid5_recover_func;
37grub_raid6_recover_func_t grub_raid6_recover_func;
38grub_diskfilter_t grub_diskfilter_list;
39static int inscnt = 0;
40static int lv_num = 0;
41
42static struct grub_diskfilter_lv *
43find_lv (const char *name);
44static int is_lv_readable (struct grub_diskfilter_lv *lv, int easily);
45
46
47
48static grub_err_t
49is_node_readable (const struct grub_diskfilter_node *node, int easily)
50{
51  /* Check whether we actually know the physical volume we want to
52     read from.  */
53  if (node->pv)
54    return !!(node->pv->disk);
55  if (node->lv)
56    return is_lv_readable (node->lv, easily);
57  return 0;
58}
59
60static int
61is_lv_readable (struct grub_diskfilter_lv *lv, int easily)
62{
63  unsigned i, j;
64  if (!lv)
65    return 0;
66  for (i = 0; i < lv->segment_count; i++)
67    {
68      int need = lv->segments[i].node_count, have = 0;
69      switch (lv->segments[i].type)
70        {
71        case GRUB_DISKFILTER_RAID6:
72          if (!easily)
73            need--;
74        case GRUB_DISKFILTER_RAID4:
75        case GRUB_DISKFILTER_RAID5:
76          if (!easily)
77            need--;
78        case GRUB_DISKFILTER_STRIPED:
79          break;
80
81        case GRUB_DISKFILTER_MIRROR:
82          need = 1;
83          break;
84
85        case GRUB_DISKFILTER_RAID10:
86          {
87            unsigned int n;
88            n = lv->segments[i].layout & 0xFF;
89            if (n == 1)
90              n = (lv->segments[i].layout >> 8) & 0xFF;
91            need = lv->segments[i].node_count - n + 1;
92          }
93          break;
94        }
95        for (j = 0; j < lv->segments[i].node_count; j++)
96          {
97            if (is_node_readable (lv->segments[i].nodes + j, easily))
98              have++;
99            if (have >= need)
100              break;
101          }
102        if (have < need)
103          return 0;
104    }
105
106  return 1;
107}
108
109static grub_err_t
110insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id,
111              struct grub_diskfilter_vg *array,
112              grub_disk_addr_t start_sector,
113              grub_diskfilter_t diskfilter __attribute__ ((unused)));
114
115static int
116is_valid_diskfilter_name (const char *name)
117{
118  return (grub_memcmp (name, "md", sizeof ("md") - 1) == 0
119          || grub_memcmp (name, "lvm/", sizeof ("lvm/") - 1) == 0
120          || grub_memcmp (name, "lvmid/", sizeof ("lvmid/") - 1) == 0
121          || grub_memcmp (name, "ldm/", sizeof ("ldm/") - 1) == 0);
122}
123
124/* Helper for scan_disk.  */
125static int
126scan_disk_partition_iter (grub_disk_t disk, grub_partition_t p, void *data)
127{
128  const char *name = data;
129  struct grub_diskfilter_vg *arr;
130  grub_disk_addr_t start_sector;
131  struct grub_diskfilter_pv_id id;
132  grub_diskfilter_t diskfilter;
133
134  grub_dprintf ("diskfilter", "Scanning for DISKFILTER devices on disk %s\n",
135                name);
136#ifdef GRUB_UTIL
137  grub_util_info ("Scanning for DISKFILTER devices on disk %s", name);
138#endif
139
140  disk->partition = p;
141 
142  for (arr = array_list; arr != NULL; arr = arr->next)
143    {
144      struct grub_diskfilter_pv *m;
145      for (m = arr->pvs; m; m = m->next)
146        if (m->disk && m->disk->id == disk->id
147            && m->disk->dev->id == disk->dev->id
148            && m->part_start == grub_partition_get_start (disk->partition)
149            && m->part_size == grub_disk_get_size (disk))
150          return 0;
151    }
152
153  for (diskfilter = grub_diskfilter_list; diskfilter; diskfilter = diskfilter->next)
154    {
155#ifdef GRUB_UTIL
156      grub_util_info ("Scanning for %s devices on disk %s", 
157                      diskfilter->name, name);
158#endif
159      id.uuid = 0;
160      id.uuidlen = 0;
161      arr = diskfilter->detect (disk, &id, &start_sector);
162      if (arr &&
163          (! insert_array (disk, &id, arr, start_sector, diskfilter)))
164        {
165          if (id.uuidlen)
166            grub_free (id.uuid);
167          return 0;
168        }
169      if (arr && id.uuidlen)
170        grub_free (id.uuid);
171
172      /* This error usually means it's not diskfilter, no need to display
173         it.  */
174      if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
175        grub_print_error ();
176
177      grub_errno = GRUB_ERR_NONE;
178    }
179
180  return 0;
181}
182
183static int
184scan_disk (const char *name, int accept_diskfilter)
185{
186  grub_disk_t disk;
187  static int scan_depth = 0;
188
189  if (!accept_diskfilter && is_valid_diskfilter_name (name))
190    return 0;
191
192  if (scan_depth > 100)
193    return 0;
194
195  scan_depth++;
196  disk = grub_disk_open (name);
197  if (!disk)
198    {
199      grub_errno = GRUB_ERR_NONE;
200      scan_depth--;
201      return 0;
202    }
203  scan_disk_partition_iter (disk, 0, (void *) name);
204  grub_partition_iterate (disk, scan_disk_partition_iter, (void *) name);
205  grub_disk_close (disk);
206  scan_depth--;
207  return 0;
208}
209
210static int
211scan_disk_hook (const char *name, void *data __attribute__ ((unused)))
212{
213  return scan_disk (name, 0);
214}
215
216static void
217scan_devices (const char *arname)
218{
219  grub_disk_dev_t p;
220  grub_disk_pull_t pull;
221  struct grub_diskfilter_vg *vg;
222  struct grub_diskfilter_lv *lv = NULL;
223  int scan_depth;
224  int need_rescan;
225
226  for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
227    for (p = grub_disk_dev_list; p; p = p->next)
228      if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
229          && p->iterate)
230        {
231          if ((p->iterate) (scan_disk_hook, NULL, pull))
232            return;
233          if (arname && is_lv_readable (find_lv (arname), 1))
234            return;
235        }
236
237  scan_depth = 0;
238  need_rescan = 1;
239  while (need_rescan && scan_depth++ < 100)
240    {
241      need_rescan = 0;
242      for (vg = array_list; vg; vg = vg->next)
243        {
244          if (vg->lvs)
245            for (lv = vg->lvs; lv; lv = lv->next)
246              if (!lv->scanned && lv->fullname && lv->became_readable_at)
247                {
248                  scan_disk (lv->fullname, 1);
249                  lv->scanned = 1;
250                  need_rescan = 1;
251                }
252        }
253    }
254
255  if (need_rescan)
256     grub_error (GRUB_ERR_UNKNOWN_DEVICE, "DISKFILTER scan depth exceeded");
257}
258
259static int
260grub_diskfilter_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
261                         grub_disk_pull_t pull)
262{
263  struct grub_diskfilter_vg *array;
264  int islcnt = 0;
265
266  if (pull == GRUB_DISK_PULL_RESCAN)
267    {
268      islcnt = inscnt + 1;
269      scan_devices (NULL);
270    }
271
272  if (pull != GRUB_DISK_PULL_NONE && pull != GRUB_DISK_PULL_RESCAN)
273    return 0;
274
275  for (array = array_list; array; array = array->next)
276    {
277      struct grub_diskfilter_lv *lv;
278      if (array->lvs)
279        for (lv = array->lvs; lv; lv = lv->next)
280          if (lv->visible && lv->fullname && lv->became_readable_at >= islcnt)
281            {
282              if (hook (lv->fullname, hook_data))
283                return 1;
284            }
285    }
286
287  return 0;
288}
289
290#ifdef GRUB_UTIL
291static grub_disk_memberlist_t
292grub_diskfilter_memberlist (grub_disk_t disk)
293{
294  struct grub_diskfilter_lv *lv = disk->data;
295  grub_disk_memberlist_t list = NULL, tmp;
296  struct grub_diskfilter_pv *pv;
297  grub_disk_pull_t pull;
298  grub_disk_dev_t p;
299  struct grub_diskfilter_vg *vg;
300  struct grub_diskfilter_lv *lv2 = NULL;
301
302  if (!lv->vg->pvs)
303    return NULL;
304
305  pv = lv->vg->pvs;
306  while (pv && pv->disk)
307    pv = pv->next;
308
309  for (pull = 0; pv && pull < GRUB_DISK_PULL_MAX; pull++)
310    for (p = grub_disk_dev_list; pv && p; p = p->next)
311      if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
312          && p->iterate)
313        {
314          (p->iterate) (scan_disk_hook, NULL, pull);
315          while (pv && pv->disk)
316            pv = pv->next;
317        }
318
319  for (vg = array_list; pv && vg; vg = vg->next)
320    {
321      if (vg->lvs)
322        for (lv2 = vg->lvs; pv && lv2; lv2 = lv2->next)
323          if (!lv2->scanned && lv2->fullname && lv2->became_readable_at)
324            {
325              scan_disk (lv2->fullname, 1);
326              lv2->scanned = 1;
327              while (pv && pv->disk)
328                pv = pv->next;
329            }
330    }
331
332  for (pv = lv->vg->pvs; pv; pv = pv->next)
333    {
334      if (!pv->disk)
335        {
336          /* TRANSLATORS: This message kicks in during the detection of
337             which modules needs to be included in core image. This happens
338             in the case of degraded RAID and means that autodetection may
339             fail to include some of modules. It's an installation time
340             message, not runtime message.  */
341          grub_util_warn (_("Couldn't find physical volume `%s'."
342                            " Some modules may be missing from core image."),
343                          pv->name);
344          continue;
345        }
346      tmp = grub_malloc (sizeof (*tmp));
347      tmp->disk = pv->disk;
348      tmp->next = list;
349      list = tmp;
350    }
351
352  return list;
353}
354
355void
356grub_diskfilter_get_partmap (grub_disk_t disk,
357                             void (*cb) (const char *pm))
358{
359  struct grub_diskfilter_lv *lv = disk->data;
360  struct grub_diskfilter_pv *pv;
361
362  if (lv->vg->pvs)
363    for (pv = lv->vg->pvs; pv; pv = pv->next)
364      {
365        grub_size_t s;
366        if (!pv->disk)
367          {
368            /* TRANSLATORS: This message kicks in during the detection of
369               which modules needs to be included in core image. This happens
370               in the case of degraded RAID and means that autodetection may
371               fail to include some of modules. It's an installation time
372               message, not runtime message.  */
373            grub_util_warn (_("Couldn't find physical volume `%s'."
374                              " Some modules may be missing from core image."),
375                            pv->name);
376            continue;
377          }
378        for (s = 0; pv->partmaps[s]; s++)
379          cb (pv->partmaps[s]);
380      }
381}
382
383static const char *
384grub_diskfilter_getname (struct grub_disk *disk)
385{
386  struct grub_diskfilter_lv *array = disk->data;
387
388  return array->vg->driver->name;
389}
390#endif
391
392static inline char
393hex2ascii (int c)
394{
395  if (c >= 10)
396    return 'a' + c - 10;
397  return c + '0';
398}
399
400static struct grub_diskfilter_lv *
401find_lv (const char *name)
402{
403  struct grub_diskfilter_vg *vg;
404  struct grub_diskfilter_lv *lv = NULL;
405
406  for (vg = array_list; vg; vg = vg->next)
407    {
408      if (vg->lvs)
409        for (lv = vg->lvs; lv; lv = lv->next)
410          if (((lv->fullname && grub_strcmp (lv->fullname, name) == 0)
411               || (lv->idname && grub_strcmp (lv->idname, name) == 0))
412              && is_lv_readable (lv, 0))
413            return lv;
414    }
415  return NULL;
416}
417
418static grub_err_t
419grub_diskfilter_open (const char *name, grub_disk_t disk)
420{
421  struct grub_diskfilter_lv *lv;
422
423  if (!is_valid_diskfilter_name (name))
424     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown DISKFILTER device %s",
425                        name);
426
427  lv = find_lv (name);
428
429  if (! lv)
430    {
431      scan_devices (name);
432      if (grub_errno)
433        {
434          grub_print_error ();
435          grub_errno = GRUB_ERR_NONE;
436        }
437      lv = find_lv (name);
438    }
439
440  if (!lv)
441    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown DISKFILTER device %s",
442                       name);
443
444  disk->id = lv->number;
445  disk->data = lv;
446
447  disk->total_sectors = lv->size;
448  disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
449  return 0;
450}
451
452static void
453grub_diskfilter_close (grub_disk_t disk __attribute ((unused)))
454{
455  return;
456}
457
458static grub_err_t
459read_lv (struct grub_diskfilter_lv *lv, grub_disk_addr_t sector,
460         grub_size_t size, char *buf);
461
462grub_err_t
463grub_diskfilter_read_node (const struct grub_diskfilter_node *node,
464                           grub_disk_addr_t sector,
465                           grub_size_t size, char *buf)
466{
467  /* Check whether we actually know the physical volume we want to
468     read from.  */
469  if (node->pv)
470    {
471      if (node->pv->disk)
472        return grub_disk_read (node->pv->disk, sector + node->start
473                               + node->pv->start_sector,
474                               0, size << GRUB_DISK_SECTOR_BITS, buf);
475      else
476        return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
477                           N_("physical volume %s not found"), node->pv->name);
478
479    }
480  if (node->lv)
481    return read_lv (node->lv, sector + node->start, size, buf);
482  return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown node '%s'", node->name);
483}
484
485static grub_err_t
486read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector,
487              grub_size_t size, char *buf)
488{
489  grub_err_t err;
490  switch (seg->type)
491    {
492    case GRUB_DISKFILTER_STRIPED:
493      if (seg->node_count == 1)
494        return grub_diskfilter_read_node (&seg->nodes[0],
495                                          sector, size, buf);
496    case GRUB_DISKFILTER_MIRROR:
497    case GRUB_DISKFILTER_RAID10:
498      {
499        grub_disk_addr_t read_sector, far_ofs;
500        grub_uint64_t disknr, b, near, far, ofs;
501        unsigned int i, j;
502           
503        read_sector = grub_divmod64 (sector, seg->stripe_size, &b);
504        far = ofs = near = 1;
505        far_ofs = 0;
506
507        if (seg->type == 1)
508          near = seg->node_count;
509        else if (seg->type == 10)
510          {
511            near = seg->layout & 0xFF;
512            far = (seg->layout >> 8) & 0xFF;
513            if (seg->layout >> 16)
514              {
515                ofs = far;
516                far_ofs = 1;
517              }
518            else
519              far_ofs = grub_divmod64 (seg->raid_member_size,
520                                       far * seg->stripe_size, 0);
521               
522            far_ofs *= seg->stripe_size;
523          }
524
525        read_sector = grub_divmod64 (read_sector * near, 
526                                     seg->node_count,
527                                     &disknr);
528
529        ofs *= seg->stripe_size;
530        read_sector *= ofs;
531       
532        while (1)
533          {
534            grub_size_t read_size;
535
536            read_size = seg->stripe_size - b;
537            if (read_size > size)
538              read_size = size;
539
540            err = 0;
541            for (i = 0; i < near; i++)
542              {
543                unsigned int k;
544
545                k = disknr;
546                err = 0;
547                for (j = 0; j < far; j++)
548                  {
549                    if (grub_errno == GRUB_ERR_READ_ERROR
550                        || grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
551                      grub_errno = GRUB_ERR_NONE;
552
553                    err = grub_diskfilter_read_node (&seg->nodes[k],
554                                                     read_sector
555                                                     + j * far_ofs + b,
556                                                     read_size,
557                                                     buf);
558                    if (! err)
559                      break;
560                    else if (err != GRUB_ERR_READ_ERROR
561                             && err != GRUB_ERR_UNKNOWN_DEVICE)
562                      return err;
563                    k++;
564                    if (k == seg->node_count)
565                      k = 0;
566                  }
567
568                if (! err)
569                  break;
570
571                disknr++;
572                if (disknr == seg->node_count)
573                  {
574                    disknr = 0;
575                    read_sector += ofs;
576                  }
577              }
578
579            if (err)
580              return err;
581
582            buf += read_size << GRUB_DISK_SECTOR_BITS;
583            size -= read_size;
584            if (! size)
585              return GRUB_ERR_NONE;
586           
587            b = 0;
588            disknr += (near - i);
589            while (disknr >= seg->node_count)
590              {
591                disknr -= seg->node_count;
592                read_sector += ofs;
593              }
594          }
595      }
596
597    case GRUB_DISKFILTER_RAID4:
598    case GRUB_DISKFILTER_RAID5:
599    case GRUB_DISKFILTER_RAID6:
600      {
601        grub_disk_addr_t read_sector;
602        grub_uint64_t b, p, n, disknr, e;
603
604        /* n = 1 for level 4 and 5, 2 for level 6.  */
605        n = seg->type / 3;
606
607        /* Find the first sector to read. */
608        read_sector = grub_divmod64 (sector, seg->stripe_size, &b);
609        read_sector = grub_divmod64 (read_sector, seg->node_count - n,
610                                     &disknr);
611        if (seg->type >= 5)
612          {
613            grub_divmod64 (read_sector, seg->node_count, &p);
614
615            if (! (seg->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
616              p = seg->node_count - 1 - p;
617
618            if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
619              {
620                disknr += p + n;
621              }
622            else
623              {
624                grub_uint32_t q;
625
626                q = p + (n - 1);
627                if (q >= seg->node_count)
628                  q -= seg->node_count;
629
630                if (disknr >= p)
631                  disknr += n;
632                else if (disknr >= q)
633                  disknr += q + 1;
634              }
635
636            if (disknr >= seg->node_count)
637              disknr -= seg->node_count;
638          }
639        else
640          p = seg->node_count - n;
641        read_sector *= seg->stripe_size;
642
643        while (1)
644          {
645            grub_size_t read_size;
646            int next_level;
647           
648            read_size = seg->stripe_size - b;
649            if (read_size > size)
650              read_size = size;
651
652            e = 0;
653            /* Reset read error.  */
654            if (grub_errno == GRUB_ERR_READ_ERROR
655                || grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
656              grub_errno = GRUB_ERR_NONE;
657
658            err = grub_diskfilter_read_node (&seg->nodes[disknr],
659                                             read_sector + b,
660                                             read_size,
661                                             buf);
662
663            if ((err) && (err != GRUB_ERR_READ_ERROR
664                          && err != GRUB_ERR_UNKNOWN_DEVICE))
665              return err;
666            e++;
667
668            if (err)
669              {
670                grub_errno = GRUB_ERR_NONE;
671                if (seg->type == GRUB_DISKFILTER_RAID6)
672                  {
673                    err = ((grub_raid6_recover_func) ?
674                           (*grub_raid6_recover_func) (seg, disknr, p,
675                                                       buf, read_sector + b,
676                                                       read_size) :
677                           grub_error (GRUB_ERR_BAD_DEVICE,
678                                       N_("module `%s' isn't loaded"),
679                                       "raid6rec"));
680                  }
681                else
682                  {
683                    err = ((grub_raid5_recover_func) ?
684                           (*grub_raid5_recover_func) (seg, disknr,
685                                                       buf, read_sector + b,
686                                                       read_size) :
687                           grub_error (GRUB_ERR_BAD_DEVICE,
688                                       N_("module `%s' isn't loaded"),
689                                       "raid5rec"));
690                  }
691
692                if (err)
693                  return err;
694              }
695
696            buf += read_size << GRUB_DISK_SECTOR_BITS;
697            size -= read_size;
698            sector += read_size;
699            if (! size)
700              break;
701
702            b = 0;
703            disknr++;
704
705            if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
706              {
707                if (disknr == seg->node_count)
708                  disknr = 0;
709
710                next_level = (disknr == p);
711              }
712            else
713              {
714                if (disknr == p)
715                  disknr += n;
716
717                next_level = (disknr >= seg->node_count);
718              }
719
720            if (next_level)
721              {
722                read_sector += seg->stripe_size;
723
724                if (seg->type >= 5)
725                  {
726                    if (seg->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
727                      p = (p == seg->node_count - 1) ? 0 : p + 1;
728                    else
729                      p = (p == 0) ? seg->node_count - 1 : p - 1;
730
731                    if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
732                      {
733                        disknr = p + n;
734                        if (disknr >= seg->node_count)
735                          disknr -= seg->node_count;
736                      }
737                    else
738                      {
739                        disknr -= seg->node_count;
740                        if ((disknr >= p && disknr < p + n)
741                            || (disknr + seg->node_count >= p
742                                && disknr + seg->node_count < p + n))
743                          disknr = p + n;
744                        if (disknr >= seg->node_count)
745                          disknr -= seg->node_count;
746                      }
747                  }
748                else
749                  disknr = 0;
750              }
751          }
752      }   
753      return GRUB_ERR_NONE;
754    default:
755      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
756                         "unsupported RAID level %d", seg->type);
757    }
758}
759
760static grub_err_t
761read_lv (struct grub_diskfilter_lv *lv, grub_disk_addr_t sector,
762         grub_size_t size, char *buf)
763{
764  if (!lv)
765    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown volume");
766
767  while (size)
768    {
769      grub_err_t err = 0;
770      struct grub_diskfilter_vg *vg = lv->vg;
771      struct grub_diskfilter_segment *seg = lv->segments;
772      grub_uint64_t extent;
773      grub_uint64_t to_read;
774
775      extent = grub_divmod64 (sector, vg->extent_size, NULL);
776     
777      /* Find the right segment.  */
778      {
779        unsigned int i;
780        for (i = 0; i < lv->segment_count; i++)
781          {
782            if ((seg->start_extent <= extent)
783                && ((seg->start_extent + seg->extent_count) > extent))
784              break;
785            seg++;
786          }
787        if (i == lv->segment_count)
788          return grub_error (GRUB_ERR_READ_ERROR, "incorrect segment");
789      }
790      to_read = ((seg->start_extent + seg->extent_count)
791                 * vg->extent_size) - sector;
792      if (to_read > size)
793        to_read = size;
794
795      err = read_segment (seg, sector - seg->start_extent * vg->extent_size,
796                          to_read, buf);
797      if (err)
798        return err;
799
800      size -= to_read;
801      sector += to_read;
802      buf += to_read << GRUB_DISK_SECTOR_BITS;
803    }
804  return GRUB_ERR_NONE;
805}
806
807static grub_err_t
808grub_diskfilter_read (grub_disk_t disk, grub_disk_addr_t sector,
809                      grub_size_t size, char *buf)
810{
811  return read_lv (disk->data, sector, size, buf);
812}
813
814static grub_err_t
815grub_diskfilter_write (grub_disk_t disk __attribute ((unused)),
816                 grub_disk_addr_t sector __attribute ((unused)),
817                 grub_size_t size __attribute ((unused)),
818                 const char *buf __attribute ((unused)))
819{
820  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
821                     "diskfilter writes are not supported");
822}
823
824struct grub_diskfilter_vg *
825grub_diskfilter_get_vg_by_uuid (grub_size_t uuidlen, char *uuid)
826{
827  struct grub_diskfilter_vg *p;
828
829  for (p = array_list; p != NULL; p = p->next)
830    if ((p->uuid_len == uuidlen) &&
831        (! grub_memcmp (p->uuid, uuid, p->uuid_len)))
832      return p;
833  return NULL;
834}
835
836grub_err_t
837grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg)
838{
839  struct grub_diskfilter_lv *lv, *p;
840  struct grub_diskfilter_vg *vgp;
841  unsigned i;
842
843  grub_dprintf ("diskfilter", "Found array %s\n", vg->name);
844#ifdef GRUB_UTIL
845  grub_util_info ("Found array %s", vg->name);
846#endif
847
848  for (lv = vg->lvs; lv; lv = lv->next)
849    {
850      lv->number = lv_num++;
851
852      if (lv->fullname)
853        {
854          grub_size_t len;
855          int max_used_number = 0, need_new_name = 0;
856          len = grub_strlen (lv->fullname);
857          for (vgp = array_list; vgp; vgp = vgp->next)
858            for (p = vgp->lvs; p; p = p->next)
859              {
860                int cur_num;
861                char *num, *end;
862                if (!p->fullname)
863                  continue;
864                if (grub_strncmp (p->fullname, lv->fullname, len) != 0)
865                  continue;
866                if (p->fullname[len] == 0)
867                  {
868                    need_new_name = 1;
869                    continue;
870                  }
871                num = p->fullname + len + 1;
872                if (!grub_isdigit (num[0]))
873                  continue;
874                cur_num = grub_strtoul (num, &end, 10);
875                if (end[0])
876                  continue;
877                if (cur_num > max_used_number)
878                  max_used_number = cur_num;
879              }
880          if (need_new_name)
881            {
882              char *tmp;
883              tmp = grub_xasprintf ("%s_%d", lv->fullname, max_used_number + 1);
884              if (!tmp)
885                return grub_errno;
886              grub_free (lv->fullname);
887              lv->fullname = tmp;
888            }
889        }
890      /* RAID 1 doesn't use a chunksize but code assumes one so set
891         one. */
892      for (i = 0; i < lv->segment_count; i++)
893        if (lv->segments[i].type == 1)
894          lv->segments[i].stripe_size = 64;
895      lv->vg = vg;
896    }
897  /* Add our new array to the list.  */
898  vg->next = array_list;
899  array_list = vg;
900  return GRUB_ERR_NONE;
901}
902
903struct grub_diskfilter_vg *
904grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb,
905                           char *name, grub_uint64_t disk_size,
906                           grub_uint64_t stripe_size,
907                           int layout, int level)
908{
909  struct grub_diskfilter_vg *array;
910  int i;
911  grub_size_t j;
912  grub_uint64_t totsize;
913  struct grub_diskfilter_pv *pv;
914  grub_err_t err;
915
916  switch (level)
917    {
918    case 1:
919      totsize = disk_size;
920      break;
921
922    case 10:
923      {
924        int n;
925        n = layout & 0xFF;
926        if (n == 1)
927          n = (layout >> 8) & 0xFF;
928
929        totsize = grub_divmod64 (nmemb * disk_size, n, 0);
930      }
931      break;
932
933    case 0:
934    case 4:
935    case 5:
936    case 6:
937      totsize = (nmemb - ((unsigned) level / 3U)) * disk_size;
938      break;
939
940    default:
941      return NULL;
942    }
943
944  array = grub_diskfilter_get_vg_by_uuid (uuidlen, uuid);
945  if (array)
946    {
947      if (array->lvs && array->lvs->size < totsize)
948        {
949          array->lvs->size = totsize;
950          if (array->lvs->segments)
951            array->lvs->segments->extent_count = totsize;
952        }
953
954      if (array->lvs->segments
955          && array->lvs->segments->raid_member_size > disk_size)
956        array->lvs->segments->raid_member_size = disk_size;
957
958      grub_free (uuid);
959      return array;
960    }
961  array = grub_zalloc (sizeof (*array));
962  if (!array)
963    return NULL;
964  array->uuid = uuid;
965  array->uuid_len = uuidlen;
966  if (name)
967    {
968      /* Strip off the homehost if present.  */
969      char *colon = grub_strchr (name, ':');
970      char *new_name = grub_xasprintf ("md/%s",
971                                       colon ? colon + 1 : name);
972
973      if (! new_name)
974        goto fail;
975
976      array->name = new_name;
977    }
978
979  array->extent_size = 1;
980  array->lvs = grub_zalloc (sizeof (*array->lvs));
981  if (!array->lvs)
982    goto fail;
983  array->lvs->segment_count = 1;
984  array->lvs->visible = 1;
985  array->lvs->name = array->name;
986  array->lvs->fullname = array->name;
987
988  array->lvs->idname = grub_malloc (sizeof ("mduuid/") + 2 * uuidlen);
989  if (!array->lvs->idname)
990    goto fail;
991
992  grub_memcpy (array->lvs->idname, "mduuid/", sizeof ("mduuid/") - 1);
993  for (j = 0; j < uuidlen; j++)
994    {
995      array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * j]
996        = hex2ascii (((unsigned char) uuid[j] >> 4));
997      array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * j + 1]
998        = hex2ascii (((unsigned char) uuid[j] & 0xf));
999    }
1000  array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * uuidlen] = '\0';
1001
1002  array->lvs->size = totsize;
1003
1004  array->lvs->segments = grub_zalloc (sizeof (*array->lvs->segments));
1005  if (!array->lvs->segments)
1006    goto fail;
1007  array->lvs->segments->stripe_size = stripe_size;
1008  array->lvs->segments->layout = layout;
1009  array->lvs->segments->start_extent = 0;
1010  array->lvs->segments->extent_count = totsize;
1011  array->lvs->segments->type = level;
1012  array->lvs->segments->node_count = nmemb;
1013  array->lvs->segments->raid_member_size = disk_size;
1014  array->lvs->segments->nodes
1015    = grub_zalloc (nmemb * sizeof (array->lvs->segments->nodes[0]));
1016  array->lvs->segments->stripe_size = stripe_size;
1017  for (i = 0; i < nmemb; i++)
1018    {
1019      pv = grub_zalloc (sizeof (*pv));
1020      if (!pv)
1021        goto fail;
1022      pv->id.uuidlen = 0;
1023      pv->id.id = i;
1024      pv->next = array->pvs;
1025      array->pvs = pv;
1026      array->lvs->segments->nodes[i].pv = pv;
1027    }
1028
1029  err = grub_diskfilter_vg_register (array);
1030  if (err)
1031    goto fail;
1032
1033  return array;
1034
1035 fail:
1036  grub_free (array->lvs);
1037  while (array->pvs)
1038    {
1039      pv = array->pvs->next;
1040      grub_free (array->pvs);
1041      array->pvs = pv;
1042    }
1043  grub_free (array);
1044  return NULL;
1045}
1046
1047static grub_err_t
1048insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id,
1049              struct grub_diskfilter_vg *array,
1050              grub_disk_addr_t start_sector,
1051              grub_diskfilter_t diskfilter __attribute__ ((unused)))
1052{
1053  struct grub_diskfilter_pv *pv;
1054
1055  grub_dprintf ("diskfilter", "Inserting %s (+%lld,%lld) into %s (%s)\n", disk->name,
1056                (unsigned long long) grub_partition_get_start (disk->partition),
1057                (unsigned long long) grub_disk_get_size (disk),
1058                array->name, diskfilter->name);
1059#ifdef GRUB_UTIL
1060  grub_util_info ("Inserting %s (+%" GRUB_HOST_PRIuLONG_LONG ",%"
1061                  GRUB_HOST_PRIuLONG_LONG ") into %s (%s)\n", disk->name,
1062                  (unsigned long long) grub_partition_get_start (disk->partition),
1063                  (unsigned long long) grub_disk_get_size (disk),
1064                  array->name, diskfilter->name);
1065  array->driver = diskfilter;
1066#endif
1067
1068  for (pv = array->pvs; pv; pv = pv->next)
1069    if (id->uuidlen == pv->id.uuidlen
1070        && id->uuidlen
1071        ? (grub_memcmp (pv->id.uuid, id->uuid, id->uuidlen) == 0) 
1072        : (pv->id.id == id->id))
1073      {
1074        struct grub_diskfilter_lv *lv;
1075        /* FIXME: Check whether the update time of the superblocks are
1076           the same.  */
1077        if (pv->disk && grub_disk_get_size (disk) >= pv->part_size)
1078          return GRUB_ERR_NONE;
1079        pv->disk = grub_disk_open (disk->name);
1080        if (!pv->disk)
1081          return grub_errno;
1082        /* This could happen to LVM on RAID, pv->disk points to the
1083           raid device, we shouldn't change it.  */
1084        pv->start_sector -= pv->part_start;
1085        pv->part_start = grub_partition_get_start (disk->partition);
1086        pv->part_size = grub_disk_get_size (disk);
1087
1088#ifdef GRUB_UTIL
1089        {
1090          grub_size_t s = 1;
1091          grub_partition_t p;
1092          for (p = disk->partition; p; p = p->parent)
1093            s++;
1094          pv->partmaps = xmalloc (s * sizeof (pv->partmaps[0]));
1095          s = 0;
1096          for (p = disk->partition; p; p = p->parent)
1097            pv->partmaps[s++] = xstrdup (p->partmap->name);
1098          pv->partmaps[s++] = 0;
1099        }
1100#endif
1101        if (start_sector != (grub_uint64_t)-1)
1102          pv->start_sector = start_sector;
1103        pv->start_sector += pv->part_start;
1104        /* Add the device to the array. */
1105        for (lv = array->lvs; lv; lv = lv->next)
1106          if (!lv->became_readable_at && lv->fullname && is_lv_readable (lv, 0))
1107            lv->became_readable_at = ++inscnt;
1108        break;
1109      }
1110
1111  return 0;
1112}
1113
1114static void
1115free_array (void)
1116{
1117  while (array_list)
1118    {
1119      struct grub_diskfilter_vg *vg;
1120      struct grub_diskfilter_pv *pv;
1121      struct grub_diskfilter_lv *lv;
1122
1123      vg = array_list;
1124      array_list = array_list->next;
1125
1126      while ((pv = vg->pvs))
1127        {
1128          vg->pvs = pv->next;
1129          grub_free (pv->name);
1130          if (pv->disk)
1131            grub_disk_close (pv->disk);
1132          if (pv->id.uuidlen)
1133            grub_free (pv->id.uuid);
1134#ifdef GRUB_UTIL
1135          grub_free (pv->partmaps);
1136#endif
1137          grub_free (pv->internal_id);
1138          grub_free (pv);
1139        }
1140
1141      while ((lv = vg->lvs))
1142        {
1143          unsigned i;
1144          vg->lvs = lv->next;
1145          if (lv->name != lv->fullname)
1146            grub_free (lv->fullname);
1147          if (lv->name != vg->name)
1148            grub_free (lv->name);
1149          for (i = 0; i < lv->segment_count; i++)
1150            grub_free (lv->segments[i].nodes);
1151          grub_free (lv->segments);
1152          grub_free (lv->internal_id);
1153          grub_free (lv);
1154        }
1155
1156      grub_free (vg->uuid);
1157      grub_free (vg->name);
1158      grub_free (vg);
1159    }
1160
1161  array_list = 0;
1162}
1163
1164#ifdef GRUB_UTIL
1165struct grub_diskfilter_pv *
1166grub_diskfilter_get_pv_from_disk (grub_disk_t disk,
1167                                  struct grub_diskfilter_vg **vg_out)
1168{
1169  struct grub_diskfilter_pv *pv;
1170  struct grub_diskfilter_vg *vg;
1171
1172  scan_disk (disk->name, 1);
1173  for (vg = array_list; vg; vg = vg->next)
1174    for (pv = vg->pvs; pv; pv = pv->next)
1175      {
1176        if (pv->disk && pv->disk->id == disk->id
1177            && pv->disk->dev->id == disk->dev->id
1178            && pv->part_start == grub_partition_get_start (disk->partition)
1179            && pv->part_size == grub_disk_get_size (disk))
1180          {
1181            if (vg_out)
1182              *vg_out = vg;
1183            return pv;
1184          }
1185      }
1186  return NULL;
1187}
1188#endif
1189
1190static struct grub_disk_dev grub_diskfilter_dev =
1191  {
1192    .name = "diskfilter",
1193    .id = GRUB_DISK_DEVICE_DISKFILTER_ID,
1194    .iterate = grub_diskfilter_iterate,
1195    .open = grub_diskfilter_open,
1196    .close = grub_diskfilter_close,
1197    .read = grub_diskfilter_read,
1198    .write = grub_diskfilter_write,
1199#ifdef GRUB_UTIL
1200    .memberlist = grub_diskfilter_memberlist,
1201    .raidname = grub_diskfilter_getname,
1202#endif
1203    .next = 0
1204  };
1205
1206
1207GRUB_MOD_INIT(diskfilter)
1208{
1209  grub_disk_dev_register (&grub_diskfilter_dev);
1210}
1211
1212GRUB_MOD_FINI(diskfilter)
1213{
1214  grub_disk_dev_unregister (&grub_diskfilter_dev);
1215  free_array ();
1216}
Note: See TracBrowser for help on using the repository browser.