hostdisk.c 10 KB

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