~ubuntu-branches/debian/sid/grub2/sid-200907171837

« back to all changes in this revision

Viewing changes to disk/lvm.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Millan
  • Date: 2006-10-14 21:19:21 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20061014211921-ge29q0dowqxicngk
Tags: 1.95-1
* New upstream release.
  - patches/03_revert_partition_numbering.diff: Delete (obsoleted).

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  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 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program 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, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include <grub/dl.h>
 
22
#include <grub/disk.h>
 
23
#include <grub/mm.h>
 
24
#include <grub/err.h>
 
25
#include <grub/misc.h>
 
26
#include <grub/lvm.h>
 
27
 
 
28
static struct grub_lvm_vg *vgs;
 
29
static int lv_count;
 
30
 
 
31
 
 
32
/* Go the string STR and return the number after STR.  *P will point
 
33
   at the number. */
 
34
static int
 
35
grub_lvm_getvalue (char **p, char *str)
 
36
{
 
37
  *p = grub_strstr (*p, str) + grub_strlen (str);
 
38
  return grub_strtoul (*p, NULL, 10);
 
39
}
 
40
 
 
41
static int
 
42
grub_lvm_iterate (int (*hook) (const char *name))
 
43
{
 
44
  struct grub_lvm_vg *vg;
 
45
  for (vg = vgs; vg; vg = vg->next)
 
46
    {
 
47
      struct grub_lvm_lv *lv;
 
48
      for (lv = vgs->lvs; lv; lv = lv->next)
 
49
        if (hook (lv->name))
 
50
          return 1;
 
51
    }
 
52
 
 
53
  return 0;
 
54
}
 
55
 
 
56
static grub_err_t
 
57
grub_lvm_open (const char *name, grub_disk_t disk)
 
58
{
 
59
  struct grub_lvm_vg *vg;
 
60
  struct grub_lvm_lv *lv = NULL;
 
61
  for (vg = vgs; vg; vg = vg->next)
 
62
    {
 
63
      for (lv = vgs->lvs; lv; lv = lv->next)
 
64
        if (! grub_strcmp (lv->name, name))
 
65
          break;
 
66
 
 
67
      if (lv)
 
68
        break;
 
69
    }
 
70
 
 
71
  if (! lv)
 
72
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown device");
 
73
 
 
74
  disk->has_partitions = 0;
 
75
  disk->id = lv->number;
 
76
  disk->data = lv;
 
77
  disk->total_sectors = lv->size;
 
78
  
 
79
  return 0;
 
80
}
 
81
 
 
82
static void
 
83
grub_lvm_close (grub_disk_t disk __attribute ((unused)))
 
84
{
 
85
  return;
 
86
}
 
87
 
 
88
static grub_err_t
 
89
grub_lvm_read (grub_disk_t disk, grub_disk_addr_t sector,
 
90
                grub_size_t size, char *buf)
 
91
{
 
92
  grub_err_t err = 0;
 
93
  struct grub_lvm_lv *lv = disk->data;
 
94
  struct grub_lvm_vg *vg = lv->vg;
 
95
  struct grub_lvm_segment *seg = lv->segments;
 
96
  struct grub_lvm_pv *pv;
 
97
  grub_uint64_t offset;
 
98
  grub_uint64_t extent;
 
99
  unsigned int i;
 
100
 
 
101
  extent = grub_divmod64 (sector, vg->extent_size, NULL);
 
102
 
 
103
  /* Find the right segment.  */
 
104
  for (i = 0; i < lv->segment_count; i++)
 
105
    {
 
106
      if ((seg->start_extent <= extent)
 
107
          && ((seg->start_extent + seg->extent_count) > extent))
 
108
        {
 
109
          break;
 
110
        }
 
111
 
 
112
      seg++;
 
113
    }
 
114
 
 
115
  if (seg->stripe_count == 1)
 
116
    {
 
117
      /* This segment is linear, so that's easy.  We just need to find
 
118
         out the offset in the physical volume and read SIZE bytes
 
119
         from that.  */
 
120
      struct grub_lvm_stripe *stripe = seg->stripes;
 
121
      grub_uint64_t seg_offset; /* Offset of the segment in PV device.  */
 
122
 
 
123
      pv = stripe->pv;
 
124
      seg_offset = ((grub_uint64_t) stripe->start
 
125
                    * (grub_uint64_t) vg->extent_size) + pv->start;
 
126
 
 
127
      offset = sector - ((grub_uint64_t) seg->start_extent
 
128
                         * (grub_uint64_t) vg->extent_size) + seg_offset;
 
129
    }
 
130
  else
 
131
    {
 
132
      /* This is a striped segment. We have to find the right PV
 
133
         similar to RAID0. */
 
134
      struct grub_lvm_stripe *stripe = seg->stripes;
 
135
      grub_uint32_t a, b;
 
136
      grub_uint64_t seg_offset; /* Offset of the segment in PV device.  */
 
137
      unsigned int stripenr;
 
138
 
 
139
      offset = sector - ((grub_uint64_t) seg->start_extent
 
140
                         * (grub_uint64_t) vg->extent_size);
 
141
 
 
142
      a = grub_divmod64 (offset, seg->stripe_size, NULL);
 
143
      grub_divmod64 (a, seg->stripe_count, &stripenr);
 
144
 
 
145
      a = grub_divmod64 (offset, seg->stripe_size * seg->stripe_count, NULL);
 
146
      grub_divmod64 (offset, seg->stripe_size, &b);
 
147
      offset = a * seg->stripe_size + b;
 
148
 
 
149
      stripe += stripenr;
 
150
      pv = stripe->pv;
 
151
      
 
152
      seg_offset = ((grub_uint64_t) stripe->start
 
153
                    * (grub_uint64_t) vg->extent_size) + pv->start;
 
154
 
 
155
      offset += seg_offset;
 
156
    }
 
157
 
 
158
  /* Check whether we actually know the physical volume we want to
 
159
     read from.  */
 
160
  if (pv->disk)
 
161
    err = grub_disk_read (pv->disk, offset, 0,
 
162
                          size << GRUB_DISK_SECTOR_BITS, buf);
 
163
  else
 
164
    err = grub_error (GRUB_ERR_UNKNOWN_DEVICE,
 
165
                      "Physical volume %s not found", pv->name);
 
166
  
 
167
  return err;
 
168
}
 
169
 
 
170
static grub_err_t
 
171
grub_lvm_write (grub_disk_t disk __attribute ((unused)),
 
172
                 grub_disk_addr_t sector __attribute ((unused)),
 
173
                 grub_size_t size __attribute ((unused)),
 
174
                 const char *buf __attribute ((unused)))
 
175
{
 
176
  return GRUB_ERR_NOT_IMPLEMENTED_YET;
 
177
}
 
178
 
 
179
static int
 
180
grub_lvm_scan_device (const char *name)
 
181
{
 
182
  grub_err_t err;
 
183
  grub_disk_t disk;
 
184
  grub_uint64_t da_offset, da_size, mda_offset, mda_size;
 
185
  char buf[GRUB_LVM_LABEL_SIZE];
 
186
  char vg_id[GRUB_LVM_ID_STRLEN+1];
 
187
  char pv_id[GRUB_LVM_ID_STRLEN+1];
 
188
  char *metadatabuf, *p, *q, *vgname;
 
189
  struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
 
190
  struct grub_lvm_pv_header *pvh;
 
191
  struct grub_lvm_disk_locn *dlocn;
 
192
  struct grub_lvm_mda_header *mdah;
 
193
  struct grub_lvm_raw_locn *rlocn;
 
194
  unsigned int i, j, vgname_len;
 
195
  struct grub_lvm_vg *vg;
 
196
  struct grub_lvm_pv *pv;
 
197
  
 
198
  disk = grub_disk_open (name);
 
199
  if (!disk)
 
200
    return 0;
 
201
 
 
202
  /* Search for label. */
 
203
  for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
 
204
    {
 
205
      err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
 
206
      if (err)
 
207
        goto fail;
 
208
      
 
209
      if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
 
210
                           sizeof (lh->id)))
 
211
          && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
 
212
                              sizeof (lh->type))))
 
213
        break;
 
214
    }
 
215
 
 
216
  /* Return if we didn't find a label. */
 
217
  if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
 
218
    goto fail;
 
219
  
 
220
  pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
 
221
 
 
222
  for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
 
223
    {
 
224
      pv_id[j++] = pvh->pv_uuid[i];
 
225
      if ((i != 1) && (i != 29) && (i % 4 == 1))
 
226
        pv_id[j++] = '-';
 
227
    }
 
228
  pv_id[j] = '\0';
 
229
 
 
230
  dlocn = pvh->disk_areas_xl;
 
231
  da_offset = grub_le_to_cpu64 (dlocn->offset);
 
232
  da_size = grub_le_to_cpu64 (dlocn->size);
 
233
 
 
234
  dlocn++;
 
235
  /* Is it possible to have multiple data/metadata areas? I haven't
 
236
     seen devices that have it. */
 
237
  if (dlocn->offset)
 
238
    {
 
239
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
240
                  "We don't support multiple data areas");
 
241
                  
 
242
      goto fail;
 
243
    }
 
244
 
 
245
  dlocn++;
 
246
  mda_offset = grub_le_to_cpu64 (dlocn->offset);
 
247
  mda_size = grub_le_to_cpu64 (dlocn->size);
 
248
  dlocn++;
 
249
  
 
250
  if (dlocn->offset)
 
251
    {
 
252
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
253
                  "We don't support multiple metadata areas");
 
254
                  
 
255
      goto fail;
 
256
    }
 
257
 
 
258
  metadatabuf = grub_malloc (mda_size);
 
259
  if (! metadatabuf)
 
260
    goto fail;
 
261
 
 
262
  err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf);
 
263
  if (err)
 
264
    goto fail2;
 
265
 
 
266
  mdah = (struct grub_lvm_mda_header *) metadatabuf;
 
267
  if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC,
 
268
                     sizeof (mdah->magic)))
 
269
      || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION))
 
270
    {
 
271
      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 
272
                  "Unknown metadata header");
 
273
      goto fail2;
 
274
    }
 
275
 
 
276
  rlocn = mdah->raw_locns;
 
277
  p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
 
278
 
 
279
  while (*q != ' ')
 
280
    q++;
 
281
 
 
282
  vgname_len = q - p;
 
283
  vgname = grub_malloc (vgname_len + 1);
 
284
  if (!vgname)
 
285
    goto fail2;
 
286
 
 
287
  grub_memcpy (vgname, p, vgname_len);
 
288
  vgname[vgname_len] = '\0';
 
289
 
 
290
  p = grub_strstr (q, "id = \"") + sizeof ("id = \"") - 1;
 
291
  grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN);
 
292
  vg_id[GRUB_LVM_ID_STRLEN] = '\0';
 
293
 
 
294
  for (vg = vgs; vg; vg = vg->next)
 
295
    {
 
296
      if (! grub_memcmp(vg_id, vg->id, GRUB_LVM_ID_STRLEN))
 
297
        break;
 
298
    }
 
299
 
 
300
  if (! vg)
 
301
    {
 
302
      /* First time we see this volume group. We've to create the
 
303
         whole volume group structure. */
 
304
      vg = grub_malloc (sizeof (*vg));
 
305
      if (! vg)
 
306
        {
 
307
          grub_free (vgname);
 
308
          goto fail;
 
309
        }
 
310
      vg->name = vgname;
 
311
      grub_memcpy (vg->id, vg_id, GRUB_LVM_ID_STRLEN+1);
 
312
 
 
313
      vg->extent_size = grub_lvm_getvalue (&p, "extent_size = ");
 
314
 
 
315
      vg->lvs = NULL;
 
316
      vg->pvs = NULL;
 
317
      vg->next = vgs;
 
318
      vgs = vg;
 
319
 
 
320
      p = grub_strstr (p, "physical_volumes {")
 
321
        + sizeof ("physical_volumes {") - 1;
 
322
 
 
323
      /* Add all the pvs to the volume group. */
 
324
      while (1)
 
325
        {
 
326
          int s;
 
327
          while (grub_isspace (*p))
 
328
            p++;
 
329
          
 
330
          if (*p == '}')
 
331
            break;
 
332
 
 
333
          pv = grub_malloc (sizeof (*pv));
 
334
          q = p;
 
335
          while (*q != ' ')
 
336
            q++;
 
337
          
 
338
          s = q - p;
 
339
          pv->name = grub_malloc (s + 1);
 
340
          grub_memcpy (pv->name, p, s);
 
341
          pv->name[s] = '\0';
 
342
 
 
343
          p = grub_strstr (p, "id = \"") + sizeof("id = \"") - 1;
 
344
 
 
345
          grub_memcpy (pv->id, p, GRUB_LVM_ID_STRLEN);
 
346
          pv->id[GRUB_LVM_ID_STRLEN] = '\0';
 
347
 
 
348
          pv->start = grub_lvm_getvalue (&p, "pe_start = ");
 
349
          pv->disk = NULL;
 
350
          pv->next = vg->pvs;
 
351
          vg->pvs = pv;
 
352
 
 
353
          p = grub_strchr (p, '}') + 1;
 
354
        }
 
355
 
 
356
      p = grub_strstr (p, "logical_volumes");
 
357
      p += 18;
 
358
      
 
359
      /* And add all the lvs to the volume group. */
 
360
      while (1)
 
361
        {
 
362
          int s;
 
363
          struct grub_lvm_lv *lv;
 
364
          struct grub_lvm_segment *seg;
 
365
          
 
366
          while (grub_isspace (*p))
 
367
            p++;
 
368
          
 
369
          if (*p == '}')
 
370
            break;
 
371
 
 
372
          lv = grub_malloc (sizeof (lv));
 
373
 
 
374
          q = p;
 
375
          while (*q != ' ')
 
376
            q++;
 
377
 
 
378
          s = q - p;
 
379
          lv->name = grub_malloc (vgname_len + 1 + s + 1);
 
380
          grub_memcpy (lv->name, vgname, vgname_len);
 
381
          lv->name[vgname_len] = '-';
 
382
          grub_memcpy (lv->name + vgname_len + 1, p, s);
 
383
          lv->name[vgname_len + 1 + s] = '\0';
 
384
 
 
385
          lv->size = 0;
 
386
          
 
387
          lv->segment_count = grub_lvm_getvalue (&p, "segment_count = ");
 
388
          lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count);
 
389
          seg = lv->segments;
 
390
 
 
391
          for (i = 0; i < lv->segment_count; i++)
 
392
            {
 
393
              struct grub_lvm_stripe *stripe;
 
394
                
 
395
              p = grub_strstr (p, "segment");
 
396
 
 
397
              seg->start_extent = grub_lvm_getvalue (&p, "start_extent = ");
 
398
              seg->extent_count = grub_lvm_getvalue (&p, "extent_count = ");
 
399
              seg->stripe_count = grub_lvm_getvalue (&p, "stripe_count = ");
 
400
 
 
401
              lv->size += seg->extent_count * vg->extent_size;
 
402
              
 
403
              if (seg->stripe_count != 1)
 
404
                seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
 
405
 
 
406
              seg->stripes = grub_malloc (sizeof (*stripe)
 
407
                                          * seg->stripe_count);
 
408
              stripe = seg->stripes;
 
409
              
 
410
              p = grub_strstr (p, "stripes = [")
 
411
                + sizeof("stripes = [") - 1;
 
412
              
 
413
              for (j = 0; j < seg->stripe_count; j++)
 
414
                {
 
415
                  char pvname[10];
 
416
                  
 
417
                  q = p = grub_strchr (p, '"') + 1;
 
418
                  while (*q != '"')
 
419
                    q++;
 
420
 
 
421
                  s = q - p;
 
422
                  grub_memcpy (pvname, p, s);
 
423
                  pvname[s] = '\0';
 
424
                  
 
425
                  for (pv = vg->pvs; pv; pv = pv->next)
 
426
                    {
 
427
                      if (! grub_strcmp (pvname, pv->name))
 
428
                        {
 
429
                          stripe->pv = pv;
 
430
                          break;
 
431
                        }
 
432
                    }
 
433
 
 
434
                  p = grub_strchr (p, ',') + 1;
 
435
                  stripe->start = grub_strtoul (p, NULL, 10);
 
436
                  
 
437
                  stripe++;
 
438
                }
 
439
 
 
440
              seg++;
 
441
            }
 
442
 
 
443
          lv->number = lv_count++;
 
444
          lv->vg = vg;
 
445
          lv->next = vg->lvs;
 
446
          vg->lvs = lv;
 
447
 
 
448
          p = grub_strchr (p, '}') + 3;
 
449
        }
 
450
    }
 
451
  else
 
452
    {
 
453
      grub_free (vgname);
 
454
    }
 
455
 
 
456
  /* Match the device we are currently reading from with the right
 
457
     PV. */
 
458
  for (pv = vg->pvs; pv; pv = pv->next)
 
459
    {
 
460
      if (! grub_memcmp (pv->id, pv_id, GRUB_LVM_ID_STRLEN))
 
461
        {
 
462
          pv->disk = grub_disk_open (name);
 
463
          break;
 
464
        }
 
465
    }
 
466
 
 
467
 fail2:
 
468
  grub_free (metadatabuf);
 
469
 fail:
 
470
  grub_disk_close (disk);
 
471
  return 0;
 
472
}
 
473
 
 
474
static struct grub_disk_dev grub_lvm_dev =
 
475
  {
 
476
    .name = "lvm",
 
477
    .id = GRUB_DISK_DEVICE_LVM_ID,
 
478
    .iterate = grub_lvm_iterate,
 
479
    .open = grub_lvm_open,
 
480
    .close = grub_lvm_close,
 
481
    .read = grub_lvm_read,
 
482
    .write = grub_lvm_write,
 
483
    .next = 0
 
484
  };
 
485
 
 
486
 
 
487
GRUB_MOD_INIT(lvm)
 
488
{
 
489
  grub_device_iterate (&grub_lvm_scan_device);
 
490
  grub_disk_dev_register (&grub_lvm_dev);
 
491
}
 
492
 
 
493
GRUB_MOD_FINI(lvm)
 
494
{
 
495
  grub_disk_dev_unregister (&grub_lvm_dev);
 
496
  /* FIXME: free the lvm list. */
 
497
}