getroot.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /* getroot.c - Get root device */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 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 <config-util.h>
  20. #include <config.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <assert.h>
  24. #include <fcntl.h>
  25. #include <unistd.h>
  26. #include <string.h>
  27. #include <dirent.h>
  28. #include <errno.h>
  29. #include <error.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <stdint.h>
  33. #ifdef HAVE_LIMITS_H
  34. #include <limits.h>
  35. #endif
  36. #include <grub/util/misc.h>
  37. #include <grub/cryptodisk.h>
  38. #include <grub/i18n.h>
  39. #ifdef __linux__
  40. #include <sys/ioctl.h> /* ioctl */
  41. #include <sys/mount.h>
  42. #endif
  43. #include <sys/types.h>
  44. #ifdef USE_LIBZFS
  45. # include <grub/util/libzfs.h>
  46. # include <grub/util/libnvpair.h>
  47. #endif
  48. #include <grub/mm.h>
  49. #include <grub/misc.h>
  50. #include <grub/emu/misc.h>
  51. #include <grub/emu/hostdisk.h>
  52. #include <grub/emu/getroot.h>
  53. #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
  54. #include <sys/mount.h>
  55. #endif
  56. #if defined(__NetBSD__) || defined(__OpenBSD__)
  57. # include <sys/ioctl.h>
  58. # include <sys/disklabel.h> /* struct disklabel */
  59. # include <sys/disk.h> /* struct dkwedge_info */
  60. #include <sys/param.h>
  61. #include <sys/mount.h>
  62. #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
  63. #if defined(__NetBSD__)
  64. # include <sys/fdio.h>
  65. #endif
  66. grub_disk_addr_t
  67. grub_util_find_partition_start (const char *dev)
  68. {
  69. #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
  70. struct stat st;
  71. grub_disk_addr_t partition_start;
  72. if (stat (dev, &st) >= 0
  73. && grub_util_device_is_mapped_stat (&st)
  74. && grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &partition_start))
  75. return partition_start;
  76. #endif
  77. return grub_util_find_partition_start_os (dev);
  78. }
  79. void
  80. grub_util_pull_device (const char *os_dev)
  81. {
  82. enum grub_dev_abstraction_types ab;
  83. ab = grub_util_get_dev_abstraction (os_dev);
  84. switch (ab)
  85. {
  86. case GRUB_DEV_ABSTRACTION_LVM:
  87. grub_util_pull_lvm_by_command (os_dev);
  88. /* Fallthrough - in case that lvm-tools are unavailable. */
  89. case GRUB_DEV_ABSTRACTION_LUKS:
  90. grub_util_pull_devmapper (os_dev);
  91. return;
  92. default:
  93. if (grub_util_pull_device_os (os_dev, ab))
  94. return;
  95. /* Fallthrough. */
  96. case GRUB_DEV_ABSTRACTION_NONE:
  97. free (grub_util_biosdisk_get_grub_dev (os_dev));
  98. return;
  99. }
  100. }
  101. char *
  102. grub_util_get_grub_dev (const char *os_dev)
  103. {
  104. char *ret;
  105. grub_util_pull_device (os_dev);
  106. ret = grub_util_get_devmapper_grub_dev (os_dev);
  107. if (ret)
  108. return ret;
  109. ret = grub_util_get_grub_dev_os (os_dev);
  110. if (ret)
  111. return ret;
  112. return grub_util_biosdisk_get_grub_dev (os_dev);
  113. }
  114. int
  115. grub_util_get_dev_abstraction (const char *os_dev)
  116. {
  117. enum grub_dev_abstraction_types ret;
  118. /* User explicitly claims that this drive is visible by BIOS. */
  119. if (grub_util_biosdisk_is_present (os_dev))
  120. return GRUB_DEV_ABSTRACTION_NONE;
  121. /* Check for LVM and LUKS. */
  122. ret = grub_util_get_dm_abstraction (os_dev);
  123. if (ret != GRUB_DEV_ABSTRACTION_NONE)
  124. return ret;
  125. return grub_util_get_dev_abstraction_os (os_dev);
  126. }
  127. static char *
  128. convert_system_partition_to_system_disk (const char *os_dev, int *is_part)
  129. {
  130. #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
  131. struct stat st;
  132. if (stat (os_dev, &st) < 0)
  133. {
  134. const char *errstr = strerror (errno);
  135. grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot stat `%s': %s"),
  136. os_dev, errstr);
  137. grub_util_info (_("cannot stat `%s': %s"), os_dev, errstr);
  138. return 0;
  139. }
  140. *is_part = 0;
  141. if (grub_util_device_is_mapped_stat (&st))
  142. return grub_util_devmapper_part_to_disk (&st, is_part, os_dev);
  143. *is_part = 0;
  144. return grub_util_part_to_disk (os_dev, &st, is_part);
  145. #else
  146. *is_part = 0;
  147. return grub_util_part_to_disk (os_dev, NULL, is_part);
  148. #endif
  149. }
  150. static const char *
  151. find_system_device (const char *os_dev)
  152. {
  153. char *os_disk;
  154. const char *drive;
  155. int is_part;
  156. os_disk = convert_system_partition_to_system_disk (os_dev, &is_part);
  157. if (! os_disk)
  158. return NULL;
  159. drive = grub_hostdisk_os_dev_to_grub_drive (os_disk, 0);
  160. free (os_disk);
  161. return drive;
  162. }
  163. static char *
  164. make_device_name (const char *drive)
  165. {
  166. char *ret, *ptr;
  167. const char *iptr;
  168. ret = xcalloc (2, strlen (drive));
  169. ptr = ret;
  170. for (iptr = drive; *iptr; iptr++)
  171. {
  172. if (*iptr == ',' || *iptr == '\\')
  173. *ptr++ = '\\';
  174. *ptr++ = *iptr;
  175. }
  176. *ptr = 0;
  177. return ret;
  178. }
  179. char *
  180. grub_util_get_os_disk (const char *os_dev)
  181. {
  182. int is_part;
  183. grub_util_info ("Looking for %s", os_dev);
  184. return convert_system_partition_to_system_disk (os_dev, &is_part);
  185. }
  186. #if !defined(__APPLE__)
  187. /* Context for grub_util_biosdisk_get_grub_dev. */
  188. struct grub_util_biosdisk_get_grub_dev_ctx
  189. {
  190. char *partname;
  191. grub_disk_addr_t start;
  192. };
  193. /* Helper for grub_util_biosdisk_get_grub_dev. */
  194. static int
  195. find_partition (grub_disk_t dsk __attribute__ ((unused)),
  196. const grub_partition_t partition, void *data)
  197. {
  198. struct grub_util_biosdisk_get_grub_dev_ctx *ctx = data;
  199. grub_disk_addr_t part_start = 0;
  200. grub_util_info ("Partition %d starts from %" GRUB_HOST_PRIuLONG_LONG,
  201. partition->number, (unsigned long long) partition->start);
  202. part_start = grub_partition_get_start (partition);
  203. if (ctx->start == part_start)
  204. {
  205. ctx->partname = grub_partition_get_name (partition);
  206. return 1;
  207. }
  208. return 0;
  209. }
  210. #endif
  211. char *
  212. grub_util_biosdisk_get_grub_dev (const char *os_dev)
  213. {
  214. const char *drive;
  215. char *sys_disk;
  216. int is_part;
  217. grub_util_info ("Looking for %s", os_dev);
  218. sys_disk = convert_system_partition_to_system_disk (os_dev, &is_part);
  219. if (!sys_disk)
  220. return 0;
  221. drive = grub_hostdisk_os_dev_to_grub_drive (sys_disk, 1);
  222. grub_util_info ("%s is a parent of %s", sys_disk, os_dev);
  223. if (!is_part)
  224. {
  225. free (sys_disk);
  226. return make_device_name (drive);
  227. }
  228. free (sys_disk);
  229. #if defined(__APPLE__)
  230. /* Apple uses "/dev/r?disk[0-9]+(s[0-9]+)?". */
  231. /*
  232. * Note: we do not use the new partition naming scheme as dos_part does not
  233. * necessarily correspond to an msdos partition.
  234. */
  235. {
  236. const char *p;
  237. char *dri, *ret;
  238. int part;
  239. int disk = (grub_memcmp (os_dev, "/dev/disk", sizeof ("/dev/disk") - 1)
  240. == 0);
  241. int rdisk = (grub_memcmp (os_dev, "/dev/rdisk", sizeof ("/dev/rdisk") - 1)
  242. == 0);
  243. dri = make_device_name (drive);
  244. if (!disk && !rdisk)
  245. return dri;
  246. p = os_dev + sizeof ("/dev/disk") + rdisk - 1;
  247. while (*p >= '0' && *p <= '9')
  248. p++;
  249. if (*p != 's')
  250. return dri;
  251. p++;
  252. part = strtol (p, NULL, 10);
  253. if (part == 0)
  254. return dri;
  255. ret = xasprintf ("%s,%d", dri, part);
  256. free (dri);
  257. return ret;
  258. }
  259. #else
  260. /* Linux counts partitions uniformly, whether a BSD partition or a DOS
  261. partition, so mapping them to GRUB devices is not trivial.
  262. Here, get the start sector of a partition by HDIO_GETGEO, and
  263. compare it with each partition GRUB recognizes.
  264. Cygwin /dev/sdXN emulation uses Windows partition mapping. It
  265. does not count the extended partition and missing primary
  266. partitions. Use same method as on Linux here.
  267. For NetBSD and FreeBSD, proceed as for Linux, except that the start
  268. sector is obtained from the disk label. */
  269. {
  270. char *name;
  271. grub_disk_t disk;
  272. struct grub_util_biosdisk_get_grub_dev_ctx ctx;
  273. name = make_device_name (drive);
  274. ctx.start = grub_util_find_partition_start (os_dev);
  275. if (grub_errno != GRUB_ERR_NONE)
  276. {
  277. free (name);
  278. return 0;
  279. }
  280. #if defined(__GNU__)
  281. /* Some versions of Hurd use badly glued Linux code to handle partitions
  282. resulting in partitions being promoted to disks. */
  283. /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
  284. /*
  285. * Note: we do not use the new partition naming scheme as dos_part does not
  286. * necessarily correspond to an msdos partition.
  287. */
  288. if (ctx.start == (grub_disk_addr_t) -1)
  289. {
  290. char *p;
  291. char *dri;
  292. dri = make_device_name (drive);
  293. p = strrchr (os_dev + sizeof ("/dev/hd") - 1, 's');
  294. if (p)
  295. {
  296. long int n;
  297. char *q;
  298. p++;
  299. n = strtol (p, &q, 10);
  300. if (p != q && n > 0 && n != GRUB_LONG_MAX)
  301. {
  302. char *t;
  303. t = dri;
  304. if (*q >= 'a' && *q <= 'g')
  305. dri = xasprintf ("%s,%ld,%d", t, n, *q - 'a' + 1);
  306. else
  307. dri = xasprintf ("%s,%ld", t, n);
  308. free (t);
  309. }
  310. }
  311. return dri;
  312. }
  313. #endif
  314. grub_util_info ("%s starts from %" GRUB_HOST_PRIuLONG_LONG,
  315. os_dev, (unsigned long long) ctx.start);
  316. grub_util_info ("opening the device %s", name);
  317. disk = grub_disk_open (name);
  318. free (name);
  319. if (! disk)
  320. {
  321. /* We already know that the partition exists. Given that we already
  322. checked the device map above, we can only get
  323. GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
  324. This can happen on Xen, where disk images in the host can be
  325. assigned to devices that have partition-like names in the guest
  326. but are really more like disks. */
  327. if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
  328. {
  329. char *canon;
  330. grub_util_warn
  331. (_("disk does not exist, so falling back to partition device %s"),
  332. os_dev);
  333. grub_errno = GRUB_ERR_NONE;
  334. canon = grub_canonicalize_file_name (os_dev);
  335. drive = grub_hostdisk_os_dev_to_grub_drive (canon ? : os_dev, 1);
  336. if (canon)
  337. free (canon);
  338. return make_device_name (drive);
  339. }
  340. else
  341. return 0;
  342. }
  343. name = grub_util_get_ldm (disk, ctx.start);
  344. if (name)
  345. {
  346. grub_disk_close (disk);
  347. return name;
  348. }
  349. ctx.partname = NULL;
  350. grub_partition_iterate (disk, find_partition, &ctx);
  351. if (grub_errno != GRUB_ERR_NONE)
  352. {
  353. grub_disk_close (disk);
  354. return 0;
  355. }
  356. if (ctx.partname == NULL)
  357. {
  358. grub_disk_close (disk);
  359. grub_util_info ("cannot find the partition of `%s'", os_dev);
  360. grub_error (GRUB_ERR_BAD_DEVICE,
  361. "cannot find the partition of `%s'", os_dev);
  362. return 0;
  363. }
  364. name = grub_xasprintf ("%s,%s", disk->name, ctx.partname);
  365. free (ctx.partname);
  366. grub_disk_close (disk);
  367. return name;
  368. }
  369. #endif
  370. }
  371. int
  372. grub_util_biosdisk_is_present (const char *os_dev)
  373. {
  374. int ret = (find_system_device (os_dev) != NULL);
  375. grub_util_info ((ret ? "%s is present" : "%s is not present"),
  376. os_dev);
  377. return ret;
  378. }
  379. #ifdef USE_LIBZFS
  380. static libzfs_handle_t *__libzfs_handle;
  381. static void
  382. fini_libzfs (void)
  383. {
  384. libzfs_fini (__libzfs_handle);
  385. }
  386. libzfs_handle_t *
  387. grub_get_libzfs_handle (void)
  388. {
  389. if (! __libzfs_handle)
  390. {
  391. __libzfs_handle = libzfs_init ();
  392. if (__libzfs_handle)
  393. atexit (fini_libzfs);
  394. }
  395. return __libzfs_handle;
  396. }
  397. #endif /* USE_LIBZFS */