hostdisk.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /* hostdisk.c - emulate biosdisk */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010,2011,2012,2013 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 <grub/disk.h>
  21. #include <grub/partition.h>
  22. #include <grub/msdos_partition.h>
  23. #include <grub/types.h>
  24. #include <grub/err.h>
  25. #include <grub/emu/misc.h>
  26. #include <grub/emu/hostdisk.h>
  27. #include <grub/emu/getroot.h>
  28. #include <grub/emu/exec.h>
  29. #include <grub/misc.h>
  30. #include <grub/i18n.h>
  31. #include <grub/list.h>
  32. #include <grub/osdep/major.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <ctype.h>
  37. #include <assert.h>
  38. #include <unistd.h>
  39. #include <sys/types.h>
  40. #include <sys/stat.h>
  41. #include <sys/wait.h>
  42. #include <fcntl.h>
  43. #include <errno.h>
  44. #include <limits.h>
  45. # include <sys/ioctl.h> /* ioctl */
  46. # include <sys/mount.h>
  47. # ifndef BLKFLSBUF
  48. # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
  49. # endif /* ! BLKFLSBUF */
  50. # include <sys/ioctl.h> /* ioctl */
  51. # ifndef HDIO_GETGEO
  52. # define HDIO_GETGEO 0x0301 /* get device geometry */
  53. /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
  54. defined. */
  55. struct hd_geometry
  56. {
  57. unsigned char heads;
  58. unsigned char sectors;
  59. unsigned short cylinders;
  60. unsigned long start;
  61. };
  62. # endif /* ! HDIO_GETGEO */
  63. # ifndef BLKGETSIZE64
  64. # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
  65. # endif /* ! BLKGETSIZE64 */
  66. grub_int64_t
  67. grub_util_get_fd_size_os (grub_util_fd_t fd, const char *name, unsigned *log_secsize)
  68. {
  69. unsigned long long nr;
  70. unsigned sector_size, log_sector_size;
  71. if (ioctl (fd, BLKGETSIZE64, &nr))
  72. return -1;
  73. if (ioctl (fd, BLKSSZGET, &sector_size))
  74. return -1;
  75. if (sector_size & (sector_size - 1) || !sector_size)
  76. return -1;
  77. log_sector_size = grub_log2ull (sector_size);
  78. if (log_secsize)
  79. *log_secsize = log_sector_size;
  80. if (nr & ((1 << log_sector_size) - 1))
  81. grub_util_error ("%s", _("unaligned device size"));
  82. return nr;
  83. }
  84. static char *
  85. sysfs_partition_path (const char *dev, const char *entry)
  86. {
  87. struct stat st;
  88. if (stat (dev, &st) == 0 && S_ISBLK (st.st_mode))
  89. return xasprintf ("/sys/dev/block/%u:%u/%s",
  90. major (st.st_rdev), minor (st.st_rdev), entry);
  91. return NULL;
  92. }
  93. static int
  94. sysfs_partition_start (const char *dev, grub_disk_addr_t *start)
  95. {
  96. char *path;
  97. FILE *fp;
  98. unsigned long long val;
  99. int ret = 0;
  100. path = sysfs_partition_path (dev, "start");
  101. if (!path)
  102. return 0;
  103. fp = grub_util_fopen (path, "r");
  104. if (!fp)
  105. goto out;
  106. if (fscanf (fp, "%llu", &val) == 1)
  107. {
  108. *start = (grub_disk_addr_t) val;
  109. ret = 1;
  110. }
  111. out:
  112. free (path);
  113. if (fp)
  114. fclose (fp);
  115. return ret;
  116. }
  117. grub_disk_addr_t
  118. grub_util_find_partition_start_os (const char *dev)
  119. {
  120. grub_disk_addr_t start = 0;
  121. grub_util_fd_t fd;
  122. struct hd_geometry hdg;
  123. if (sysfs_partition_start (dev, &start))
  124. return start;
  125. fd = open (dev, O_RDONLY);
  126. if (fd == -1)
  127. {
  128. grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
  129. dev, strerror (errno));
  130. return 0;
  131. }
  132. if (ioctl (fd, HDIO_GETGEO, &hdg))
  133. {
  134. grub_error (GRUB_ERR_BAD_DEVICE,
  135. "cannot get disk geometry of `%s'", dev);
  136. close (fd);
  137. return 0;
  138. }
  139. close (fd);
  140. return hdg.start;
  141. }
  142. /* Cache of partition start sectors for each disk. */
  143. struct linux_partition_cache
  144. {
  145. struct linux_partition_cache *next;
  146. struct linux_partition_cache **prev;
  147. char *dev;
  148. unsigned long start;
  149. int partno;
  150. };
  151. struct linux_partition_cache *linux_partition_cache_list;
  152. /* Check if we have devfs support. */
  153. static int
  154. have_devfs (void)
  155. {
  156. static int dev_devfsd_exists = -1;
  157. if (dev_devfsd_exists < 0)
  158. {
  159. struct stat st;
  160. dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
  161. }
  162. return dev_devfsd_exists;
  163. }
  164. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  165. static int
  166. grub_hostdisk_linux_find_partition (const grub_disk_t disk, char *dev,
  167. grub_disk_addr_t sector)
  168. {
  169. size_t len = strlen (dev);
  170. const char *format;
  171. char *p;
  172. int i;
  173. char real_dev[PATH_MAX];
  174. struct linux_partition_cache *cache;
  175. int missing = 0;
  176. strcpy(real_dev, dev);
  177. if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
  178. {
  179. p = real_dev + len - 4;
  180. format = "part%d";
  181. }
  182. else if (strncmp (real_dev, "/dev/disk/by-id/",
  183. sizeof ("/dev/disk/by-id/") - 1) == 0)
  184. {
  185. p = real_dev + len;
  186. format = "-part%d";
  187. }
  188. else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
  189. {
  190. p = real_dev + len;
  191. format = "p%d";
  192. }
  193. else
  194. {
  195. p = real_dev + len;
  196. format = "%d";
  197. }
  198. for (cache = linux_partition_cache_list; cache; cache = cache->next)
  199. {
  200. if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
  201. {
  202. sprintf (p, format, cache->partno);
  203. strcpy (dev, real_dev);
  204. return 1;
  205. }
  206. }
  207. for (i = 1; i < 10000; i++)
  208. {
  209. grub_util_fd_t fd;
  210. grub_disk_addr_t start;
  211. struct stat st;
  212. sprintf (p, format, i);
  213. fd = open (real_dev, O_RDONLY);
  214. if (fd == -1)
  215. {
  216. if (missing++ < 10)
  217. continue;
  218. else
  219. return 0;
  220. }
  221. missing = 0;
  222. if (fstat (fd, &st) < 0
  223. || !grub_util_device_is_mapped_stat (&st)
  224. || !grub_util_get_dm_node_linear_info (st.st_rdev, 0, 0, &start))
  225. start = grub_disk_to_native_sector (disk, grub_util_find_partition_start_os (real_dev));
  226. /* We don't care about errors here. */
  227. grub_errno = GRUB_ERR_NONE;
  228. close (fd);
  229. if (start == sector)
  230. {
  231. struct linux_partition_cache *new_cache_item;
  232. new_cache_item = xmalloc (sizeof *new_cache_item);
  233. new_cache_item->dev = xstrdup (dev);
  234. new_cache_item->start = start;
  235. new_cache_item->partno = i;
  236. grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
  237. GRUB_AS_LIST (new_cache_item));
  238. strcpy (dev, real_dev);
  239. return 1;
  240. }
  241. }
  242. return 0;
  243. }
  244. #pragma GCC diagnostic error "-Wformat-nonliteral"
  245. void
  246. grub_hostdisk_flush_initial_buffer (const char *os_dev)
  247. {
  248. grub_util_fd_t fd;
  249. struct stat st;
  250. fd = open (os_dev, O_RDONLY);
  251. if (fd >= 0 && fstat (fd, &st) >= 0 && S_ISBLK (st.st_mode))
  252. ioctl (fd, BLKFLSBUF, 0);
  253. if (fd >= 0)
  254. close (fd);
  255. }
  256. int
  257. grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
  258. grub_disk_addr_t *max)
  259. {
  260. grub_util_fd_t fd;
  261. struct grub_util_hostdisk_data *data = disk->data;
  262. *max = ~0ULL;
  263. #ifdef O_LARGEFILE
  264. flags |= O_LARGEFILE;
  265. #endif
  266. #ifdef O_SYNC
  267. flags |= O_SYNC;
  268. #endif
  269. #ifdef O_FSYNC
  270. flags |= O_FSYNC;
  271. #endif
  272. #ifdef O_BINARY
  273. flags |= O_BINARY;
  274. #endif
  275. #ifdef O_CLOEXEC
  276. flags |= O_CLOEXEC;
  277. #endif
  278. /* Linux has a bug that the disk cache for a whole disk is not consistent
  279. with the one for a partition of the disk. */
  280. {
  281. int is_partition = 0;
  282. char dev[PATH_MAX];
  283. grub_disk_addr_t part_start = 0;
  284. part_start = grub_partition_get_start (disk->partition)
  285. >> (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
  286. strncpy (dev, grub_util_biosdisk_get_osdev (disk), sizeof (dev) - 1);
  287. dev[sizeof(dev) - 1] = '\0';
  288. if (disk->partition
  289. && strncmp (dev, "/dev/", 5) == 0)
  290. {
  291. if (sector >= part_start)
  292. is_partition = grub_hostdisk_linux_find_partition (disk, dev, part_start);
  293. else
  294. *max = part_start - sector;
  295. }
  296. reopen:
  297. if (data->dev && strcmp (data->dev, dev) == 0 &&
  298. data->access_mode == (flags & O_ACCMODE))
  299. {
  300. grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev);
  301. fd = data->fd;
  302. }
  303. else
  304. {
  305. free (data->dev);
  306. data->dev = 0;
  307. if (data->fd != -1)
  308. {
  309. if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
  310. {
  311. fsync (data->fd);
  312. if (data->is_disk)
  313. ioctl (data->fd, BLKFLSBUF, 0);
  314. }
  315. close (data->fd);
  316. data->fd = -1;
  317. }
  318. /* Open the partition. */
  319. grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev);
  320. fd = open (dev, flags);
  321. if (fd < 0)
  322. {
  323. grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"),
  324. dev, strerror (errno));
  325. return -1;
  326. }
  327. data->dev = xstrdup (dev);
  328. data->access_mode = (flags & O_ACCMODE);
  329. data->fd = fd;
  330. if (data->is_disk)
  331. ioctl (data->fd, BLKFLSBUF, 0);
  332. }
  333. if (is_partition)
  334. {
  335. *max = grub_util_get_fd_size (fd, dev, 0);
  336. *max >>= disk->log_sector_size;
  337. if (sector - part_start >= *max)
  338. {
  339. *max = disk->partition->len - (sector - part_start);
  340. if (*max == 0)
  341. *max = ~0ULL;
  342. is_partition = 0;
  343. strncpy (dev, grub_util_biosdisk_get_osdev (disk), sizeof (dev) - 1);
  344. dev[sizeof(dev) - 1] = '\0';
  345. goto reopen;
  346. }
  347. sector -= part_start;
  348. *max -= sector;
  349. }
  350. }
  351. if (grub_util_fd_seek (fd, sector << disk->log_sector_size))
  352. {
  353. close (fd);
  354. grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"),
  355. grub_util_biosdisk_get_osdev (disk), strerror (errno));
  356. return -1;
  357. }
  358. return fd;
  359. }