]> git.proxmox.com Git - grub2.git/blob - util/hostdisk.c
2009-11-02 Samuel Thibault <samuel.thibault@ens-lyon.org>
[grub2.git] / util / hostdisk.c
1 /* hostdisk.c - emulate biosdisk */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,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
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/util/misc.h>
26 #include <grub/util/hostdisk.h>
27 #include <grub/misc.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <assert.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <limits.h>
40
41 #ifdef __linux__
42 # include <sys/ioctl.h> /* ioctl */
43 # if !defined(__GLIBC__) || \
44 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
45 /* Maybe libc doesn't have large file support. */
46 # include <linux/unistd.h> /* _llseek */
47 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
48 # ifndef BLKFLSBUF
49 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
50 # endif /* ! BLKFLSBUF */
51 # include <sys/ioctl.h> /* ioctl */
52 # ifndef HDIO_GETGEO
53 # define HDIO_GETGEO 0x0301 /* get device geometry */
54 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
55 defined. */
56 struct hd_geometry
57 {
58 unsigned char heads;
59 unsigned char sectors;
60 unsigned short cylinders;
61 unsigned long start;
62 };
63 # endif /* ! HDIO_GETGEO */
64 # ifndef BLKGETSIZE64
65 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
66 # endif /* ! BLKGETSIZE64 */
67 # ifndef MAJOR
68 # ifndef MINORBITS
69 # define MINORBITS 8
70 # endif /* ! MINORBITS */
71 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
72 # endif /* ! MAJOR */
73 # ifndef FLOPPY_MAJOR
74 # define FLOPPY_MAJOR 2
75 # endif /* ! FLOPPY_MAJOR */
76 # ifndef LOOP_MAJOR
77 # define LOOP_MAJOR 7
78 # endif /* ! LOOP_MAJOR */
79 #endif /* __linux__ */
80
81 #ifdef __CYGWIN__
82 # include <sys/ioctl.h>
83 # include <cygwin/fs.h> /* BLKGETSIZE64 */
84 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
85 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
86 # define FLOPPY_MAJOR 2
87 #endif
88
89 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
90 # include <sys/disk.h> /* DIOCGMEDIASIZE */
91 # include <sys/param.h>
92 # include <sys/sysctl.h>
93 #endif
94
95 #if defined(__APPLE__)
96 # include <sys/disk.h>
97 #endif
98
99 struct
100 {
101 char *drive;
102 char *device;
103 } map[256];
104
105 #ifdef __linux__
106 /* Check if we have devfs support. */
107 static int
108 have_devfs (void)
109 {
110 static int dev_devfsd_exists = -1;
111
112 if (dev_devfsd_exists < 0)
113 {
114 struct stat st;
115
116 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
117 }
118
119 return dev_devfsd_exists;
120 }
121 #endif /* __linux__ */
122
123 static int
124 find_grub_drive (const char *name)
125 {
126 unsigned int i;
127
128 if (name)
129 {
130 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
131 if (map[i].drive && ! strcmp (map[i].drive, name))
132 return i;
133 }
134
135 return -1;
136 }
137
138 static int
139 find_free_slot ()
140 {
141 unsigned int i;
142
143 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
144 if (! map[i].drive)
145 return i;
146
147 return -1;
148 }
149
150 static int
151 grub_util_biosdisk_iterate (int (*hook) (const char *name))
152 {
153 unsigned i;
154
155 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
156 if (map[i].drive && hook (map[i].drive))
157 return 1;
158
159 return 0;
160 }
161
162 static grub_err_t
163 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
164 {
165 int drive;
166 struct stat st;
167
168 drive = find_grub_drive (name);
169 if (drive < 0)
170 return grub_error (GRUB_ERR_BAD_DEVICE,
171 "no mapping exists for `%s'", name);
172
173 disk->has_partitions = 1;
174 disk->id = drive;
175
176 /* Get the size. */
177 #if defined(__MINGW32__)
178 {
179 grub_uint64_t size;
180
181 size = grub_util_get_disk_size (map[drive].device);
182
183 if (size % 512)
184 grub_util_error ("unaligned device size");
185
186 disk->total_sectors = size >> 9;
187
188 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
189
190 return GRUB_ERR_NONE;
191 }
192 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
193 defined(__FreeBSD_kernel__) || defined(__APPLE__)
194 {
195 unsigned long long nr;
196 int fd;
197
198 fd = open (map[drive].device, O_RDONLY);
199 if (fd == -1)
200 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
201
202 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
203 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
204 # else
205 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
206 # endif
207 {
208 close (fd);
209 goto fail;
210 }
211
212 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
213 if (ioctl (fd, DIOCGMEDIASIZE, &nr))
214 # elif defined(__APPLE__)
215 if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
216 # else
217 if (ioctl (fd, BLKGETSIZE64, &nr))
218 # endif
219 {
220 close (fd);
221 goto fail;
222 }
223
224 close (fd);
225
226 #if defined (__APPLE__)
227 disk->total_sectors = nr;
228 #else
229 disk->total_sectors = nr / 512;
230
231 if (nr % 512)
232 grub_util_error ("unaligned device size");
233 #endif
234
235 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
236
237 return GRUB_ERR_NONE;
238 }
239
240 fail:
241 /* In GNU/Hurd, stat() will return the right size. */
242 #elif !defined (__GNU__)
243 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
244 #endif
245 if (stat (map[drive].device, &st) < 0)
246 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
247
248 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
249
250 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
251
252 return GRUB_ERR_NONE;
253 }
254
255 #ifdef __linux__
256 static int
257 linux_find_partition (char *dev, unsigned long sector)
258 {
259 size_t len = strlen (dev);
260 const char *format;
261 char *p;
262 int i;
263 char real_dev[PATH_MAX];
264
265 strcpy(real_dev, dev);
266
267 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
268 {
269 p = real_dev + len - 4;
270 format = "part%d";
271 }
272 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
273 {
274 p = real_dev + len;
275 format = "p%d";
276 }
277 else
278 {
279 p = real_dev + len;
280 format = "%d";
281 }
282
283 for (i = 1; i < 10000; i++)
284 {
285 int fd;
286 struct hd_geometry hdg;
287
288 sprintf (p, format, i);
289 fd = open (real_dev, O_RDONLY);
290 if (fd == -1)
291 return 0;
292
293 if (ioctl (fd, HDIO_GETGEO, &hdg))
294 {
295 close (fd);
296 return 0;
297 }
298
299 close (fd);
300
301 if (hdg.start == sector)
302 {
303 strcpy (dev, real_dev);
304 return 1;
305 }
306 }
307
308 return 0;
309 }
310 #endif /* __linux__ */
311
312 static int
313 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
314 {
315 int fd;
316
317 #ifdef O_LARGEFILE
318 flags |= O_LARGEFILE;
319 #endif
320 #ifdef O_SYNC
321 flags |= O_SYNC;
322 #endif
323 #ifdef O_FSYNC
324 flags |= O_FSYNC;
325 #endif
326 #ifdef O_BINARY
327 flags |= O_BINARY;
328 #endif
329
330 #ifdef __linux__
331 /* Linux has a bug that the disk cache for a whole disk is not consistent
332 with the one for a partition of the disk. */
333 {
334 int is_partition = 0;
335 char dev[PATH_MAX];
336
337 strcpy (dev, map[disk->id].device);
338 if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0)
339 is_partition = linux_find_partition (dev, disk->partition->start);
340
341 /* Open the partition. */
342 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev);
343 fd = open (dev, flags);
344 if (fd < 0)
345 {
346 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
347 return -1;
348 }
349
350 /* Make the buffer cache consistent with the physical disk. */
351 ioctl (fd, BLKFLSBUF, 0);
352
353 if (is_partition)
354 sector -= disk->partition->start;
355 }
356 #else /* ! __linux__ */
357 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
358 int sysctl_flags, sysctl_oldflags;
359 size_t sysctl_size = sizeof (sysctl_flags);
360
361 if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
362 {
363 grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
364 return -1;
365 }
366 sysctl_flags = sysctl_oldflags | 0x10;
367 if (! (sysctl_oldflags & 0x10)
368 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
369 {
370 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
371 return -1;
372 }
373 #endif
374
375 fd = open (map[disk->id].device, flags);
376
377 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
378 if (! (sysctl_oldflags & 0x10)
379 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
380 {
381 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
382 return -1;
383 }
384 #endif
385
386 #if defined(__APPLE__)
387 /* If we can't have exclusive access, try shared access */
388 if (fd < 0)
389 fd = open(map[disk->id].device, flags | O_SHLOCK);
390 #endif
391
392 if (fd < 0)
393 {
394 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
395 return -1;
396 }
397 #endif /* ! __linux__ */
398
399 #if defined(__linux__) && (!defined(__GLIBC__) || \
400 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
401 /* Maybe libc doesn't have large file support. */
402 {
403 loff_t offset, result;
404 static int _llseek (uint filedes, ulong hi, ulong lo,
405 loff_t *res, uint wh);
406 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
407 loff_t *, res, uint, wh);
408
409 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
410 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
411 {
412 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
413 close (fd);
414 return -1;
415 }
416 }
417 #else
418 {
419 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
420
421 if (lseek (fd, offset, SEEK_SET) != offset)
422 {
423 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
424 close (fd);
425 return -1;
426 }
427 }
428 #endif
429
430 return fd;
431 }
432
433 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
434 error occurs, otherwise return LEN. */
435 static ssize_t
436 nread (int fd, char *buf, size_t len)
437 {
438 ssize_t size = len;
439
440 while (len)
441 {
442 ssize_t ret = read (fd, buf, len);
443
444 if (ret <= 0)
445 {
446 if (errno == EINTR)
447 continue;
448 else
449 return ret;
450 }
451
452 len -= ret;
453 buf += ret;
454 }
455
456 return size;
457 }
458
459 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
460 error occurs, otherwise return LEN. */
461 static ssize_t
462 nwrite (int fd, const char *buf, size_t len)
463 {
464 ssize_t size = len;
465
466 while (len)
467 {
468 ssize_t ret = write (fd, buf, len);
469
470 if (ret <= 0)
471 {
472 if (errno == EINTR)
473 continue;
474 else
475 return ret;
476 }
477
478 len -= ret;
479 buf += ret;
480 }
481
482 return size;
483 }
484
485 static grub_err_t
486 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
487 grub_size_t size, char *buf)
488 {
489 int fd;
490
491 fd = open_device (disk, sector, O_RDONLY);
492 if (fd < 0)
493 return grub_errno;
494
495 #ifdef __linux__
496 if (sector == 0 && size > 1)
497 {
498 /* Work around a bug in Linux ez remapping. Linux remaps all
499 sectors that are read together with the MBR in one read. It
500 should only remap the MBR, so we split the read in two
501 parts. -jochen */
502 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
503 {
504 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
505 close (fd);
506 return grub_errno;
507 }
508
509 buf += GRUB_DISK_SECTOR_SIZE;
510 size--;
511 }
512 #endif /* __linux__ */
513
514 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
515 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
516 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
517
518 close (fd);
519 return grub_errno;
520 }
521
522 static grub_err_t
523 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
524 grub_size_t size, const char *buf)
525 {
526 int fd;
527
528 fd = open_device (disk, sector, O_WRONLY);
529 if (fd < 0)
530 return grub_errno;
531
532 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
533 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
534 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
535
536 close (fd);
537 return grub_errno;
538 }
539
540 static struct grub_disk_dev grub_util_biosdisk_dev =
541 {
542 .name = "biosdisk",
543 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
544 .iterate = grub_util_biosdisk_iterate,
545 .open = grub_util_biosdisk_open,
546 .close = 0,
547 .read = grub_util_biosdisk_read,
548 .write = grub_util_biosdisk_write,
549 .next = 0
550 };
551
552 static void
553 read_device_map (const char *dev_map)
554 {
555 FILE *fp;
556 char buf[1024]; /* XXX */
557 int lineno = 0;
558 struct stat st;
559
560 auto void show_error (const char *msg);
561 void show_error (const char *msg)
562 {
563 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
564 }
565
566 fp = fopen (dev_map, "r");
567 if (! fp)
568 grub_util_error ("Cannot open `%s'", dev_map);
569
570 while (fgets (buf, sizeof (buf), fp))
571 {
572 char *p = buf;
573 char *e;
574 int drive;
575
576 lineno++;
577
578 /* Skip leading spaces. */
579 while (*p && isspace (*p))
580 p++;
581
582 /* If the first character is `#' or NUL, skip this line. */
583 if (*p == '\0' || *p == '#')
584 continue;
585
586 if (*p != '(')
587 show_error ("No open parenthesis found");
588
589 p++;
590 /* Find a free slot. */
591 drive = find_free_slot ();
592 if (drive < 0)
593 show_error ("Map table size exceeded");
594
595 e = p;
596 p = strchr (p, ')');
597 if (! p)
598 show_error ("No close parenthesis found");
599
600 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
601 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
602 map[drive].drive[p - e] = '\0';
603
604 p++;
605 /* Skip leading spaces. */
606 while (*p && isspace (*p))
607 p++;
608
609 if (*p == '\0')
610 show_error ("No filename found");
611
612 /* NUL-terminate the filename. */
613 e = p;
614 while (*e && ! isspace (*e))
615 e++;
616 *e = '\0';
617
618 #ifdef __MINGW32__
619 (void) st;
620 if (grub_util_get_disk_size (p) == -1LL)
621 #else
622 if (stat (p, &st) == -1)
623 #endif
624 {
625 free (map[drive].drive);
626 map[drive].drive = NULL;
627 grub_util_info ("Cannot stat `%s', skipping", p);
628 continue;
629 }
630
631 #ifdef __linux__
632 /* On Linux, the devfs uses symbolic links horribly, and that
633 confuses the interface very much, so use realpath to expand
634 symbolic links. */
635 map[drive].device = xmalloc (PATH_MAX);
636 if (! realpath (p, map[drive].device))
637 grub_util_error ("Cannot get the real path of `%s'", p);
638 #else
639 map[drive].device = xstrdup (p);
640 #endif
641 }
642
643 fclose (fp);
644 }
645
646 void
647 grub_util_biosdisk_init (const char *dev_map)
648 {
649 read_device_map (dev_map);
650 grub_disk_dev_register (&grub_util_biosdisk_dev);
651 }
652
653 void
654 grub_util_biosdisk_fini (void)
655 {
656 unsigned i;
657
658 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
659 {
660 if (map[i].drive)
661 free (map[i].drive);
662 if (map[i].device)
663 free (map[i].device);
664 map[i].drive = map[i].device = NULL;
665 }
666
667 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
668 }
669
670 static char *
671 make_device_name (int drive, int dos_part, int bsd_part)
672 {
673 int len = strlen(map[drive].drive);
674 char *p;
675
676 if (dos_part >= 0)
677 {
678 /* Add in char length of dos_part+1 */
679 int tmp = dos_part + 1;
680 len++;
681 while ((tmp /= 10) != 0)
682 len++;
683 }
684 if (bsd_part >= 0)
685 len += 2;
686
687 /* Length to alloc is: char length of map[drive].drive, plus
688 * char length of (dos_part+1) or of bsd_part, plus
689 * 2 for the comma and a null/end of string (\0)
690 */
691 p = xmalloc (len + 2);
692 sprintf (p, "%s", map[drive].drive);
693
694 if (dos_part >= 0)
695 sprintf (p + strlen (p), ",%d", dos_part + 1);
696
697 if (bsd_part >= 0)
698 sprintf (p + strlen (p), ",%c", bsd_part + 'a');
699
700 return p;
701 }
702
703 static char *
704 convert_system_partition_to_system_disk (const char *os_dev)
705 {
706 #if defined(__linux__)
707 char *path = xmalloc (PATH_MAX);
708 if (! realpath (os_dev, path))
709 return 0;
710
711 if (strncmp ("/dev/", path, 5) == 0)
712 {
713 char *p = path + 5;
714
715 /* If this is an IDE disk. */
716 if (strncmp ("ide/", p, 4) == 0)
717 {
718 p = strstr (p, "part");
719 if (p)
720 strcpy (p, "disc");
721
722 return path;
723 }
724
725 /* If this is a SCSI disk. */
726 if (strncmp ("scsi/", p, 5) == 0)
727 {
728 p = strstr (p, "part");
729 if (p)
730 strcpy (p, "disc");
731
732 return path;
733 }
734
735 /* If this is a DAC960 disk. */
736 if (strncmp ("rd/c", p, 4) == 0)
737 {
738 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
739 p = strchr (p, 'p');
740 if (p)
741 *p = '\0';
742
743 return path;
744 }
745
746 /* If this is a Mylex AcceleRAID Array. */
747 if (strncmp ("rs/c", p, 4) == 0)
748 {
749 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
750 p = strchr (p, 'p');
751 if (p)
752 *p = '\0';
753
754 return path;
755 }
756 /* If this is a CCISS disk. */
757 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
758 {
759 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
760 p = strchr (p, 'p');
761 if (p)
762 *p = '\0';
763
764 return path;
765 }
766
767 /* If this is a Compaq Intelligent Drive Array. */
768 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
769 {
770 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
771 p = strchr (p, 'p');
772 if (p)
773 *p = '\0';
774
775 return path;
776 }
777
778 /* If this is an I2O disk. */
779 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
780 {
781 /* /dev/i2o/hd[a-z]([0-9]+)? */
782 p[sizeof ("i2o/hda") - 1] = '\0';
783 return path;
784 }
785
786 /* If this is a MultiMediaCard (MMC). */
787 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
788 {
789 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
790 p = strchr (p, 'p');
791 if (p)
792 *p = '\0';
793
794 return path;
795 }
796
797 /* If this is an IDE, SCSI or Virtio disk. */
798 if (strncmp ("vdisk", p, 5) == 0
799 && p[5] >= 'a' && p[5] <= 'z')
800 {
801 /* /dev/vdisk[a-z][0-9]* */
802 p[6] = '\0';
803 return path;
804 }
805 if ((strncmp ("hd", p, 2) == 0
806 || strncmp ("vd", p, 2) == 0
807 || strncmp ("sd", p, 2) == 0)
808 && p[2] >= 'a' && p[2] <= 'z')
809 {
810 /* /dev/[hsv]d[a-z][0-9]* */
811 p[3] = '\0';
812 return path;
813 }
814
815 /* If this is a Xen virtual block device. */
816 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
817 {
818 /* /dev/xvd[a-z][0-9]* */
819 p[4] = '\0';
820 return path;
821 }
822 }
823
824 return path;
825
826 #elif defined(__GNU__)
827 char *path = xstrdup (os_dev);
828 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
829 {
830 char *p = strchr (path + 7, 's');
831 if (p)
832 *p = '\0';
833 }
834 return path;
835
836 #elif defined(__CYGWIN__)
837 char *path = xstrdup (os_dev);
838 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
839 path[8] = 0;
840 return path;
841
842 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
843 char *path = xstrdup (os_dev);
844 if (strncmp ("/dev/", path, 5) == 0)
845 {
846 char *p;
847 for (p = path + 5; *p; ++p)
848 if (grub_isdigit(*p))
849 {
850 p = strchr (p, 's');
851 if (p)
852 *p = '\0';
853 break;
854 }
855 }
856 return path;
857
858 #else
859 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
860 return xstrdup (os_dev);
861 #endif
862 }
863
864 #if defined(__linux__) || defined(__CYGWIN__)
865 static int
866 device_is_wholedisk (const char *os_dev)
867 {
868 int len = strlen (os_dev);
869
870 if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
871 return 1;
872 return 0;
873 }
874 #endif
875
876 static int
877 find_system_device (const char *os_dev)
878 {
879 int i;
880 char *os_disk;
881
882 os_disk = convert_system_partition_to_system_disk (os_dev);
883 if (! os_disk)
884 return -1;
885
886 for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
887 if (map[i].device && strcmp (map[i].device, os_disk) == 0)
888 {
889 free (os_disk);
890 return i;
891 }
892
893 free (os_disk);
894 return -1;
895 }
896
897 char *
898 grub_util_biosdisk_get_grub_dev (const char *os_dev)
899 {
900 struct stat st;
901 int drive;
902
903 if (stat (os_dev, &st) < 0)
904 {
905 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
906 return 0;
907 }
908
909 drive = find_system_device (os_dev);
910 if (drive < 0)
911 {
912 grub_error (GRUB_ERR_BAD_DEVICE,
913 "no mapping exists for `%s'", os_dev);
914 return 0;
915 }
916
917 if (grub_strcmp (os_dev, convert_system_partition_to_system_disk (os_dev))
918 == 0)
919 return make_device_name (drive, -1, -1);
920
921 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
922 if (! S_ISCHR (st.st_mode))
923 #else
924 if (! S_ISBLK (st.st_mode))
925 #endif
926 return make_device_name (drive, -1, -1);
927
928 #if defined(__linux__) || defined(__CYGWIN__)
929 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
930 partition, so mapping them to GRUB devices is not trivial.
931 Here, get the start sector of a partition by HDIO_GETGEO, and
932 compare it with each partition GRUB recognizes.
933
934 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
935 does not count the extended partition and missing primary
936 partitions. Use same method as on Linux here. */
937 {
938 char *name;
939 grub_disk_t disk;
940 int fd;
941 struct hd_geometry hdg;
942 int dos_part = -1;
943 int bsd_part = -1;
944 auto int find_partition (grub_disk_t disk,
945 const grub_partition_t partition);
946
947 int find_partition (grub_disk_t disk __attribute__ ((unused)),
948 const grub_partition_t partition)
949 {
950 struct grub_msdos_partition *pcdata = NULL;
951
952 if (strcmp (partition->partmap->name, "part_msdos") == 0)
953 pcdata = partition->data;
954
955 if (pcdata)
956 {
957 if (pcdata->bsd_part < 0)
958 grub_util_info ("DOS partition %d starts from %lu",
959 pcdata->dos_part, partition->start);
960 else
961 grub_util_info ("BSD partition %d,%c starts from %lu",
962 pcdata->dos_part, pcdata->bsd_part + 'a',
963 partition->start);
964 }
965 else
966 {
967 grub_util_info ("Partition %d starts from %lu",
968 partition->index, partition->start);
969 }
970
971 if (hdg.start == partition->start)
972 {
973 if (pcdata)
974 {
975 dos_part = pcdata->dos_part;
976 bsd_part = pcdata->bsd_part;
977 }
978 else
979 {
980 dos_part = partition->index;
981 bsd_part = -1;
982 }
983 return 1;
984 }
985
986 return 0;
987 }
988
989 name = make_device_name (drive, -1, -1);
990
991 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
992 return name;
993
994 fd = open (os_dev, O_RDONLY);
995 if (fd == -1)
996 {
997 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk geometry", os_dev);
998 free (name);
999 return 0;
1000 }
1001
1002 if (ioctl (fd, HDIO_GETGEO, &hdg))
1003 {
1004 grub_error (GRUB_ERR_BAD_DEVICE,
1005 "cannot get geometry of `%s'", os_dev);
1006 close (fd);
1007 free (name);
1008 return 0;
1009 }
1010
1011 close (fd);
1012
1013 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
1014
1015 if (hdg.start == 0 && device_is_wholedisk (os_dev))
1016 return name;
1017
1018 grub_util_info ("opening the device %s", name);
1019 disk = grub_disk_open (name);
1020 free (name);
1021
1022 if (! disk)
1023 return 0;
1024
1025 grub_partition_iterate (disk, find_partition);
1026 if (grub_errno != GRUB_ERR_NONE)
1027 {
1028 grub_disk_close (disk);
1029 return 0;
1030 }
1031
1032 if (dos_part < 0)
1033 {
1034 grub_disk_close (disk);
1035 grub_error (GRUB_ERR_BAD_DEVICE,
1036 "cannot find the partition of `%s'", os_dev);
1037 return 0;
1038 }
1039
1040 return make_device_name (drive, dos_part, bsd_part);
1041 }
1042
1043 #elif defined(__GNU__)
1044 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
1045 {
1046 char *p;
1047 int dos_part = -1;
1048 int bsd_part = -1;
1049
1050 p = strrchr (os_dev, 's');
1051 if (p)
1052 {
1053 long int n;
1054 char *q;
1055
1056 p++;
1057 n = strtol (p, &q, 10);
1058 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1059 {
1060 dos_part = (int) n - 1;
1061
1062 if (*q >= 'a' && *q <= 'g')
1063 bsd_part = *q - 'a';
1064 }
1065 }
1066
1067 return make_device_name (drive, dos_part, bsd_part);
1068 }
1069
1070 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
1071 /* FreeBSD uses "/dev/[a-z]+[0-9]+(s[0-9]+[a-z]?)?". */
1072 {
1073 int dos_part = -1;
1074 int bsd_part = -1;
1075
1076 if (strncmp ("/dev/", os_dev, 5) == 0)
1077 {
1078 const char *p;
1079 char *q;
1080 long int n;
1081
1082 for (p = os_dev + 5; *p; ++p)
1083 if (grub_isdigit(*p))
1084 {
1085 p = strchr (p, 's');
1086 if (p)
1087 {
1088 p++;
1089 n = strtol (p, &q, 10);
1090 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1091 {
1092 dos_part = (int) n - 1;
1093
1094 if (*q >= 'a' && *q <= 'g')
1095 bsd_part = *q - 'a';
1096 }
1097 }
1098 break;
1099 }
1100 }
1101
1102 return make_device_name (drive, dos_part, bsd_part);
1103 }
1104
1105 #else
1106 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1107 return make_device_name (drive, -1, -1);
1108 #endif
1109 }