~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/disk/lvm.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* lvm.c - module to read Logical Volumes.  */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2006,2007,2008,2009  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/lvm.h>
 
26
 
 
27
#ifdef GRUB_UTIL
 
28
#include <grub/emu/misc.h>
 
29
#endif
 
30
 
 
31
static struct grub_lvm_vg *vg_list;
 
32
static int lv_count;
 
33
 
 
34
 
 
35
/* Go the string STR and return the number after STR.  *P will point
 
36
   at the number.  In case STR is not found, *P will be NULL and the
 
37
   return value will be 0.  */
 
38
static int
 
39
grub_lvm_getvalue (char **p, char *str)
 
40
{
 
41
  *p = grub_strstr (*p, str);
 
42
  if (! *p)
 
43
    return 0;
 
44
  *p += grub_strlen (str);
 
45
  return grub_strtoul (*p, NULL, 10);
 
46
}
 
47
 
 
48
static int
 
49
grub_lvm_checkvalue (char **p, char *str, char *tmpl)
 
50
{
 
51
  int tmpllen = grub_strlen (tmpl);
 
52
  *p = grub_strstr (*p, str);
 
53
  if (! *p)
 
54
    return 0;
 
55
  *p += grub_strlen (str);
 
56
  if (**p != '"')
 
57
    return 0;
 
58
  return (grub_memcmp (*p + 1, tmpl, tmpllen) == 0 && (*p)[tmpllen + 1] == '"');
 
59
}
 
60
 
 
61
static int
 
62
grub_lvm_check_flag (char *p, char *str, char *flag)
 
63
{
 
64
  int len_str = grub_strlen (str), len_flag = grub_strlen (flag);
 
65
  while (1)
 
66
    {
 
67
      char *q;
 
68
      p = grub_strstr (p, str);
 
69
      if (! p)
 
70
        return 0;
 
71
      p += len_str;
 
72
      if (grub_memcmp (p, " = [", sizeof (" = [") - 1) != 0)
 
73
        continue;
 
74
      q = p + sizeof (" = [") - 1;
 
75
      while (1)
 
76
        {
 
77
          while (grub_isspace (*q))
 
78
            q++;
 
79
          if (*q != '"')
 
80
            return 0;
 
81
          q++;
 
82
          if (grub_memcmp (q, flag, len_flag) == 0 && q[len_flag] == '"')
 
83
            return 1;
 
84
          while (*q != '"')
 
85
            q++;
 
86
          q++;
 
87
          if (*q == ']')
 
88
            return 0;
 
89
          q++;
 
90
        }
 
91
    }
 
92
}
 
93
 
 
94
static int
 
95
grub_lvm_iterate (int (*hook) (const char *name))
 
96
{
 
97
  struct grub_lvm_vg *vg;
 
98
  for (vg = vg_list; vg; vg = vg->next)
 
99
    {
 
100
      struct grub_lvm_lv *lv;
 
101
      if (vg->lvs)
 
102
        for (lv = vg->lvs; lv; lv = lv->next)
 
103
          if (hook (lv->name))
 
104
            return 1;
 
105
    }
 
106
 
 
107
  return 0;
 
108
}
 
109
 
 
110
#ifdef GRUB_UTIL
 
111
static grub_disk_memberlist_t
 
112
grub_lvm_memberlist (grub_disk_t disk)
 
113
{
 
114
  struct grub_lvm_lv *lv = disk->data;
 
115
  grub_disk_memberlist_t list = NULL, tmp;
 
116
  struct grub_lvm_pv *pv;
 
117
 
 
118
  if (lv->vg->pvs)
 
119
    for (pv = lv->vg->pvs; pv; pv = pv->next)
 
120
      {
 
121
        if (!pv->disk)
 
122
          grub_util_error ("Couldn't find PV %s. Check your device.map",
 
123
                           pv->name);
 
124
        tmp = grub_malloc (sizeof (*tmp));
 
125
        tmp->disk = pv->disk;
 
126
        tmp->next = list;
 
127
        list = tmp;
 
128
      }
 
129
 
 
130
  return list;
 
131
}
 
132
#endif
 
133
 
 
134
static grub_err_t
 
135
grub_lvm_open (const char *name, grub_disk_t disk)
 
136
{
 
137
  struct grub_lvm_vg *vg;
 
138
  struct grub_lvm_lv *lv = NULL;
 
139
  for (vg = vg_list; vg; vg = vg->next)
 
140
    {
 
141
      if (vg->lvs)
 
142
        for (lv = vg->lvs; lv; lv = lv->next)
 
143
          if (! grub_strcmp (lv->name, name))
 
144
            break;
 
145
 
 
146
      if (lv)
 
147
        break;
 
148
    }
 
149
 
 
150
  if (! lv)
 
151
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown LVM device %s", name);
 
152
 
 
153
  disk->id = lv->number;
 
154
  disk->data = lv;
 
155
  disk->total_sectors = lv->size;
 
156
 
 
157
  return 0;
 
158
}
 
159
 
 
160
static void
 
161
grub_lvm_close (grub_disk_t disk __attribute ((unused)))
 
162
{
 
163
  return;
 
164
}
 
165
 
 
166
static grub_err_t
 
167
grub_lvm_read (grub_disk_t disk, grub_disk_addr_t sector,
 
168
                grub_size_t size, char *buf)
 
169
{
 
170
  grub_err_t err = 0;
 
171
  struct grub_lvm_lv *lv = disk->data;
 
172
  struct grub_lvm_vg *vg = lv->vg;
 
173
  struct grub_lvm_segment *seg = lv->segments;
 
174
  struct grub_lvm_pv *pv;
 
175
  grub_uint64_t offset;
 
176
  grub_uint64_t extent;
 
177
  unsigned int i;
 
178
 
 
179
  extent = grub_divmod64 (sector, vg->extent_size, NULL);
 
180
 
 
181
  /* Find the right segment.  */
 
182
  for (i = 0; i < lv->segment_count; i++)
 
183
    {
 
184
      if ((seg->start_extent <= extent)
 
185
          && ((seg->start_extent + seg->extent_count) > extent))
 
186
        {
 
187
          break;
 
188
        }
 
189
 
 
190
      seg++;
 
191
    }
 
192
 
 
193
  if (seg->stripe_count == 1)
 
194
    {
 
195
      /* This segment is linear, so that's easy.  We just need to find
 
196
         out the offset in the physical volume and read SIZE bytes
 
197
         from that.  */
 
198
      struct grub_lvm_stripe *stripe = seg->stripes;
 
199
      grub_uint64_t seg_offset; /* Offset of the segment in PV device.  */
 
200
 
 
201
      pv = stripe->pv;
 
202
      seg_offset = ((grub_uint64_t) stripe->start
 
203
                    * (grub_uint64_t) vg->extent_size) + pv->start;
 
204
 
 
205
      offset = sector - ((grub_uint64_t) seg->start_extent
 
206
                         * (grub_uint64_t) vg->extent_size) + seg_offset;
 
207
    }
 
208
  else
 
209
    {
 
210
      /* This is a striped segment. We have to find the right PV
 
211
         similar to RAID0. */
 
212
      struct grub_lvm_stripe *stripe = seg->stripes;
 
213
      grub_uint32_t a, b;
 
214
      grub_uint64_t seg_offset; /* Offset of the segment in PV device.  */
 
215
      unsigned int stripenr;
 
216
 
 
217
      offset = sector - ((grub_uint64_t) seg->start_extent
 
218
                         * (grub_uint64_t) vg->extent_size);
 
219
 
 
220
      a = grub_divmod64 (offset, seg->stripe_size, NULL);
 
221
      grub_divmod64 (a, seg->stripe_count, &stripenr);
 
222
 
 
223
      a = grub_divmod64 (offset, seg->stripe_size * seg->stripe_count, NULL);
 
224
      grub_divmod64 (offset, seg->stripe_size, &b);
 
225
      offset = a * seg->stripe_size + b;
 
226
 
 
227
      stripe += stripenr;
 
228
      pv = stripe->pv;
 
229
 
 
230
      seg_offset = ((grub_uint64_t) stripe->start
 
231
                    * (grub_uint64_t) vg->extent_size) + pv->start;
 
232
 
 
233
      offset += seg_offset;
 
234
    }
 
235
 
 
236
  /* Check whether we actually know the physical volume we want to
 
237
     read from.  */
 
238
  if (pv->disk)
 
239
    err = grub_disk_read (pv->disk, offset, 0,
 
240
                          size << GRUB_DISK_SECTOR_BITS, buf);
 
241
  else
 
242
    err = grub_error (GRUB_ERR_UNKNOWN_DEVICE,
 
243
                      "physical volume %s not found", pv->name);
 
244
 
 
245
  return err;
 
246
}
 
247
 
 
248
static grub_err_t
 
249
grub_lvm_write (grub_disk_t disk __attribute ((unused)),
 
250
                 grub_disk_addr_t sector __attribute ((unused)),
 
251
                 grub_size_t size __attribute ((unused)),
 
252
                 const char *buf __attribute ((unused)))
 
253
{
 
254
  return GRUB_ERR_NOT_IMPLEMENTED_YET;
 
255
}
 
256
 
 
257
static int
 
258
grub_lvm_scan_device (const char *name)
 
259
{
 
260
  grub_err_t err;
 
261
  grub_disk_t disk;
 
262
  grub_uint64_t mda_offset, mda_size;
 
263
  char buf[GRUB_LVM_LABEL_SIZE];
 
264
  char vg_id[GRUB_LVM_ID_STRLEN+1];
 
265
  char pv_id[GRUB_LVM_ID_STRLEN+1];
 
266
  char *metadatabuf, *p, *q, *vgname;
 
267
  struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
 
268
  struct grub_lvm_pv_header *pvh;
 
269
  struct grub_lvm_disk_locn *dlocn;
 
270
  struct grub_lvm_mda_header *mdah;
 
271
  struct grub_lvm_raw_locn *rlocn;
 
272
  unsigned int i, j, vgname_len;
 
273
  struct grub_lvm_vg *vg;
 
274
  struct grub_lvm_pv *pv;
 
275
 
 
276
#ifdef GRUB_UTIL
 
277
  grub_util_info ("scanning %s for LVM", name);
 
278
#endif
 
279
 
 
280
  disk = grub_disk_open (name);
 
281
  if (!disk)
 
282
    {
 
283
      if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
284
        grub_errno = GRUB_ERR_NONE;
 
285
      return 0;
 
286
    }
 
287
 
 
288
  /* Search for label. */
 
289
  for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
 
290
    {
 
291
      err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
 
292
      if (err)
 
293
        goto fail;
 
294
 
 
295
      if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
 
296
                           sizeof (lh->id)))
 
297
          && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
 
298
                              sizeof (lh->type))))
 
299
        break;
 
300
    }
 
301
 
 
302
  /* Return if we didn't find a label. */
 
303
  if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
 
304
    {
 
305
#ifdef GRUB_UTIL
 
306
      grub_util_info ("no LVM signature found\n");
 
307
#endif
 
308
      goto fail;
 
309
    }
 
310
 
 
311
  pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
 
312
 
 
313
  for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
 
314
    {
 
315
      pv_id[j++] = pvh->pv_uuid[i];
 
316
      if ((i != 1) && (i != 29) && (i % 4 == 1))
 
317
        pv_id[j++] = '-';
 
318
    }
 
319
  pv_id[j] = '\0';
 
320
 
 
321
  dlocn = pvh->disk_areas_xl;
 
322
 
 
323
  dlocn++;
 
324
  /* Is it possible to have multiple data/metadata areas? I haven't
 
325
     seen devices that have it. */
 
326
  if (dlocn->offset)
 
327
    {
 
328
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
329
                  "we don't support multiple LVM data areas");
 
330
 
 
331
#ifdef GRUB_UTIL
 
332
      grub_util_info ("we don't support multiple LVM data areas\n");
 
333
#endif
 
334
      goto fail;
 
335
    }
 
336
 
 
337
  dlocn++;
 
338
  mda_offset = grub_le_to_cpu64 (dlocn->offset);
 
339
  mda_size = grub_le_to_cpu64 (dlocn->size);
 
340
 
 
341
  /* It's possible to have multiple copies of metadata areas, we just use the
 
342
     first one.  */
 
343
 
 
344
  /* Allocate buffer space for the circular worst-case scenario. */
 
345
  metadatabuf = grub_malloc (2 * mda_size);
 
346
  if (! metadatabuf)
 
347
    goto fail;
 
348
 
 
349
  err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf);
 
350
  if (err)
 
351
    goto fail2;
 
352
 
 
353
  mdah = (struct grub_lvm_mda_header *) metadatabuf;
 
354
  if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC,
 
355
                     sizeof (mdah->magic)))
 
356
      || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION))
 
357
    {
 
358
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
359
                  "unknown LVM metadata header");
 
360
#ifdef GRUB_UTIL
 
361
      grub_util_info ("unknown LVM metadata header\n");
 
362
#endif
 
363
      goto fail2;
 
364
    }
 
365
 
 
366
  rlocn = mdah->raw_locns;
 
367
  if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) >
 
368
      grub_le_to_cpu64 (mdah->size))
 
369
    {
 
370
      /* Metadata is circular. Copy the wrap in place. */
 
371
      grub_memcpy (metadatabuf + mda_size,
 
372
                   metadatabuf + GRUB_LVM_MDA_HEADER_SIZE,
 
373
                   grub_le_to_cpu64 (rlocn->offset) +
 
374
                   grub_le_to_cpu64 (rlocn->size) -
 
375
                   grub_le_to_cpu64 (mdah->size));
 
376
    }
 
377
  p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
 
378
 
 
379
  while (*q != ' ' && q < metadatabuf + mda_size)
 
380
    q++;
 
381
 
 
382
  if (q == metadatabuf + mda_size)
 
383
    {
 
384
#ifdef GRUB_UTIL
 
385
      grub_util_info ("error parsing metadata\n");
 
386
#endif
 
387
      goto fail2;
 
388
    }
 
389
 
 
390
  vgname_len = q - p;
 
391
  vgname = grub_malloc (vgname_len + 1);
 
392
  if (!vgname)
 
393
    goto fail2;
 
394
 
 
395
  grub_memcpy (vgname, p, vgname_len);
 
396
  vgname[vgname_len] = '\0';
 
397
 
 
398
  p = grub_strstr (q, "id = \"");
 
399
  if (p == NULL)
 
400
    {
 
401
#ifdef GRUB_UTIL
 
402
      grub_util_info ("couldn't find ID\n");
 
403
#endif
 
404
      goto fail3;
 
405
    }
 
406
  p += sizeof ("id = \"") - 1;
 
407
  grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN);
 
408
  vg_id[GRUB_LVM_ID_STRLEN] = '\0';
 
409
 
 
410
  for (vg = vg_list; vg; vg = vg->next)
 
411
    {
 
412
      if (! grub_memcmp(vg_id, vg->id, GRUB_LVM_ID_STRLEN))
 
413
        break;
 
414
    }
 
415
 
 
416
  if (! vg)
 
417
    {
 
418
      /* First time we see this volume group. We've to create the
 
419
         whole volume group structure. */
 
420
      vg = grub_malloc (sizeof (*vg));
 
421
      if (! vg)
 
422
        goto fail3;
 
423
      vg->name = vgname;
 
424
      grub_memcpy (vg->id, vg_id, GRUB_LVM_ID_STRLEN+1);
 
425
 
 
426
      vg->extent_size = grub_lvm_getvalue (&p, "extent_size = ");
 
427
      if (p == NULL)
 
428
        {
 
429
#ifdef GRUB_UTIL
 
430
          grub_util_info ("unknown extent size\n");
 
431
#endif
 
432
          goto fail4;
 
433
        }
 
434
 
 
435
      vg->lvs = NULL;
 
436
      vg->pvs = NULL;
 
437
 
 
438
      p = grub_strstr (p, "physical_volumes {");
 
439
      if (p)
 
440
        {
 
441
          p += sizeof ("physical_volumes {") - 1;
 
442
 
 
443
          /* Add all the pvs to the volume group. */
 
444
          while (1)
 
445
            {
 
446
              int s;
 
447
              while (grub_isspace (*p))
 
448
                p++;
 
449
 
 
450
              if (*p == '}')
 
451
                break;
 
452
 
 
453
              pv = grub_malloc (sizeof (*pv));
 
454
              q = p;
 
455
              while (*q != ' ')
 
456
                q++;
 
457
 
 
458
              s = q - p;
 
459
              pv->name = grub_malloc (s + 1);
 
460
              grub_memcpy (pv->name, p, s);
 
461
              pv->name[s] = '\0';
 
462
 
 
463
              p = grub_strstr (p, "id = \"");
 
464
              if (p == NULL)
 
465
                goto pvs_fail;
 
466
              p += sizeof("id = \"") - 1;
 
467
 
 
468
              grub_memcpy (pv->id, p, GRUB_LVM_ID_STRLEN);
 
469
              pv->id[GRUB_LVM_ID_STRLEN] = '\0';
 
470
 
 
471
              pv->start = grub_lvm_getvalue (&p, "pe_start = ");
 
472
              if (p == NULL)
 
473
                {
 
474
#ifdef GRUB_UTIL
 
475
                  grub_util_info ("unknown pe_start\n");
 
476
#endif
 
477
                  goto pvs_fail;
 
478
                }
 
479
 
 
480
              p = grub_strchr (p, '}');
 
481
              if (p == NULL)
 
482
                {
 
483
#ifdef GRUB_UTIL
 
484
                  grub_util_info ("error parsing pe_start\n");
 
485
#endif
 
486
                  goto pvs_fail;
 
487
                }
 
488
              p++;
 
489
 
 
490
              pv->disk = NULL;
 
491
              pv->next = vg->pvs;
 
492
              vg->pvs = pv;
 
493
 
 
494
              continue;
 
495
            pvs_fail:
 
496
              grub_free (pv->name);
 
497
              grub_free (pv);
 
498
              goto fail4;
 
499
            }
 
500
        }
 
501
 
 
502
      p = grub_strstr (p, "logical_volumes");
 
503
      if (p)
 
504
        {
 
505
          p += 18;
 
506
 
 
507
          /* And add all the lvs to the volume group. */
 
508
          while (1)
 
509
            {
 
510
              int s;
 
511
              int skip_lv = 0;
 
512
              struct grub_lvm_lv *lv;
 
513
              struct grub_lvm_segment *seg;
 
514
 
 
515
              while (grub_isspace (*p))
 
516
                p++;
 
517
 
 
518
              if (*p == '}')
 
519
                break;
 
520
 
 
521
              lv = grub_malloc (sizeof (*lv));
 
522
 
 
523
              q = p;
 
524
              while (*q != ' ')
 
525
                q++;
 
526
 
 
527
              s = q - p;
 
528
              lv->name = grub_malloc (vgname_len + 1 + s + 1);
 
529
              grub_memcpy (lv->name, vgname, vgname_len);
 
530
              lv->name[vgname_len] = '-';
 
531
              grub_memcpy (lv->name + vgname_len + 1, p, s);
 
532
              lv->name[vgname_len + 1 + s] = '\0';
 
533
 
 
534
              lv->size = 0;
 
535
 
 
536
              if (!grub_lvm_check_flag (p, "status", "VISIBLE"))
 
537
                {
 
538
                  skip_lv = 1;
 
539
                  goto lv_parsed;
 
540
                }
 
541
 
 
542
              lv->segment_count = grub_lvm_getvalue (&p, "segment_count = ");
 
543
              if (p == NULL)
 
544
                {
 
545
#ifdef GRUB_UTIL
 
546
                  grub_util_info ("unknown segment_count\n");
 
547
#endif
 
548
                  goto lvs_fail;
 
549
                }
 
550
              lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count);
 
551
              seg = lv->segments;
 
552
 
 
553
              for (i = 0; i < lv->segment_count; i++)
 
554
                {
 
555
                  struct grub_lvm_stripe *stripe;
 
556
 
 
557
                  p = grub_strstr (p, "segment");
 
558
                  if (p == NULL)
 
559
                    {
 
560
#ifdef GRUB_UTIL
 
561
                      grub_util_info ("unknown segment\n");
 
562
#endif
 
563
                      goto lvs_segment_fail;
 
564
                    }
 
565
 
 
566
                  seg->start_extent = grub_lvm_getvalue (&p, "start_extent = ");
 
567
                  if (p == NULL)
 
568
                    {
 
569
#ifdef GRUB_UTIL
 
570
                      grub_util_info ("unknown start_extent\n");
 
571
#endif
 
572
                      goto lvs_segment_fail;
 
573
                    }
 
574
                  seg->extent_count = grub_lvm_getvalue (&p, "extent_count = ");
 
575
                  if (p == NULL)
 
576
                    {
 
577
#ifdef GRUB_UTIL
 
578
                      grub_util_info ("unknown extent_count\n");
 
579
#endif
 
580
                      goto lvs_segment_fail;
 
581
                    }
 
582
 
 
583
                  if (grub_lvm_checkvalue (&p, "type = ", "snapshot"))
 
584
                    {
 
585
                      /* Found a snapshot, give up and move on. */
 
586
                      skip_lv = 1;
 
587
                      break;
 
588
                    }
 
589
 
 
590
                  seg->stripe_count = grub_lvm_getvalue (&p, "stripe_count = ");
 
591
                  if (p == NULL)
 
592
                    {
 
593
#ifdef GRUB_UTIL
 
594
                      grub_util_info ("unknown stripe_count\n");
 
595
#endif
 
596
                      goto lvs_segment_fail;
 
597
                    }
 
598
 
 
599
                  lv->size += seg->extent_count * vg->extent_size;
 
600
 
 
601
                  if (seg->stripe_count != 1)
 
602
                    seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
 
603
 
 
604
                  seg->stripes = grub_malloc (sizeof (*stripe)
 
605
                                              * seg->stripe_count);
 
606
                  stripe = seg->stripes;
 
607
 
 
608
                  p = grub_strstr (p, "stripes = [");
 
609
                  if (p == NULL)
 
610
                    {
 
611
#ifdef GRUB_UTIL
 
612
                      grub_util_info ("unknown stripes\n");
 
613
#endif
 
614
                      goto lvs_segment_fail2;
 
615
                    }
 
616
                  p += sizeof("stripes = [") - 1;
 
617
 
 
618
                  for (j = 0; j < seg->stripe_count; j++)
 
619
                    {
 
620
                      char *pvname;
 
621
 
 
622
                      p = grub_strchr (p, '"');
 
623
                      if (p == NULL)
 
624
                        continue;
 
625
                      q = ++p;
 
626
                      while (*q != '"')
 
627
                        q++;
 
628
 
 
629
                      s = q - p;
 
630
 
 
631
                      pvname = grub_malloc (s + 1);
 
632
                      if (pvname == NULL)
 
633
                        goto lvs_segment_fail2;
 
634
 
 
635
                      grub_memcpy (pvname, p, s);
 
636
                      pvname[s] = '\0';
 
637
 
 
638
                      if (vg->pvs)
 
639
                        for (pv = vg->pvs; pv; pv = pv->next)
 
640
                          {
 
641
                            if (! grub_strcmp (pvname, pv->name))
 
642
                              {
 
643
                                stripe->pv = pv;
 
644
                                break;
 
645
                              }
 
646
                          }
 
647
 
 
648
                      grub_free(pvname);
 
649
 
 
650
                      stripe->start = grub_lvm_getvalue (&p, ",");
 
651
                      if (p == NULL)
 
652
                        continue;
 
653
 
 
654
                      stripe++;
 
655
                    }
 
656
 
 
657
                  seg++;
 
658
 
 
659
                  continue;
 
660
                lvs_segment_fail2:
 
661
                  grub_free (seg->stripes);
 
662
                lvs_segment_fail:
 
663
                  goto fail4;
 
664
                }
 
665
 
 
666
            lv_parsed:
 
667
              if (p != NULL)
 
668
                p = grub_strchr (p, '}');
 
669
              if (p == NULL)
 
670
                goto lvs_fail;
 
671
              p += 3;
 
672
 
 
673
              if (skip_lv)
 
674
                {
 
675
                  grub_free (lv->name);
 
676
                  grub_free (lv);
 
677
                  continue;
 
678
                }
 
679
 
 
680
              lv->number = lv_count++;
 
681
              lv->vg = vg;
 
682
              lv->next = vg->lvs;
 
683
              vg->lvs = lv;
 
684
 
 
685
              continue;
 
686
            lvs_fail:
 
687
              grub_free (lv->name);
 
688
              grub_free (lv);
 
689
              goto fail4;
 
690
            }
 
691
        }
 
692
 
 
693
        vg->next = vg_list;
 
694
        vg_list = vg;
 
695
    }
 
696
  else
 
697
    {
 
698
      grub_free (vgname);
 
699
    }
 
700
 
 
701
  /* Match the device we are currently reading from with the right
 
702
     PV. */
 
703
  if (vg->pvs)
 
704
    for (pv = vg->pvs; pv; pv = pv->next)
 
705
      {
 
706
        if (! grub_memcmp (pv->id, pv_id, GRUB_LVM_ID_STRLEN))
 
707
          {
 
708
            /* This could happen to LVM on RAID, pv->disk points to the
 
709
               raid device, we shouldn't change it.  */
 
710
            if (! pv->disk)
 
711
              pv->disk = grub_disk_open (name);
 
712
            break;
 
713
          }
 
714
      }
 
715
 
 
716
  goto fail2;
 
717
 
 
718
  /* Failure path.  */
 
719
 fail4:
 
720
  grub_free (vg);
 
721
 fail3:
 
722
  grub_free (vgname);
 
723
 
 
724
  /* Normal exit path.  */
 
725
 fail2:
 
726
  grub_free (metadatabuf);
 
727
 fail:
 
728
  grub_disk_close (disk);
 
729
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
730
    grub_errno = GRUB_ERR_NONE;
 
731
  return 0;
 
732
}
 
733
 
 
734
static struct grub_disk_dev grub_lvm_dev =
 
735
  {
 
736
    .name = "lvm",
 
737
    .id = GRUB_DISK_DEVICE_LVM_ID,
 
738
    .iterate = grub_lvm_iterate,
 
739
    .open = grub_lvm_open,
 
740
    .close = grub_lvm_close,
 
741
    .read = grub_lvm_read,
 
742
    .write = grub_lvm_write,
 
743
#ifdef GRUB_UTIL
 
744
    .memberlist = grub_lvm_memberlist,
 
745
#endif
 
746
    .next = 0
 
747
  };
 
748
 
 
749
 
 
750
GRUB_MOD_INIT(lvm)
 
751
{
 
752
  grub_device_iterate (&grub_lvm_scan_device);
 
753
  if (grub_errno)
 
754
    {
 
755
      grub_print_error ();
 
756
      grub_errno = GRUB_ERR_NONE;
 
757
    }
 
758
 
 
759
  grub_disk_dev_register (&grub_lvm_dev);
 
760
}
 
761
 
 
762
GRUB_MOD_FINI(lvm)
 
763
{
 
764
  grub_disk_dev_unregister (&grub_lvm_dev);
 
765
  vg_list = NULL;
 
766
  /* FIXME: free the lvm list. */
 
767
}