lvm.c 15 KB

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