Merge branch 'mainline' into zfs
[grub2/phcoder.git] / util / hostdisk.c
blob1844a7e55b5b9f50cb244d0ed71357d94d5e60ae
1 /* biosdisk.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.
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.
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.
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/>.
20 #include <grub/disk.h>
21 #include <grub/partition.h>
22 #include <grub/pc_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>
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>
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
58 unsigned char heads;
59 unsigned char sectors;
60 unsigned short cylinders;
61 unsigned long start;
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__ */
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
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
95 struct
97 char *drive;
98 char *device;
99 } map[256];
101 #ifdef __linux__
102 /* Check if we have devfs support. */
103 static int
104 have_devfs (void)
106 static int dev_devfsd_exists = -1;
108 if (dev_devfsd_exists < 0)
110 struct stat st;
112 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
115 return dev_devfsd_exists;
117 #endif /* __linux__ */
119 static int
120 find_grub_drive (const char *name)
122 unsigned int i;
124 if (name)
126 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
127 if (map[i].drive && ! strcmp (map[i].drive, name))
128 return i;
131 return -1;
134 static int
135 find_free_slot ()
137 unsigned int i;
139 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
140 if (! map[i].drive)
141 return i;
143 return -1;
146 static int
147 grub_util_biosdisk_iterate (int (*hook) (const char *name))
149 unsigned i;
151 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
152 if (map[i].drive && hook (map[i].drive))
153 return 1;
155 return 0;
158 static grub_err_t
159 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
161 int drive;
162 struct stat st;
164 drive = find_grub_drive (name);
165 if (drive < 0)
166 return grub_error (GRUB_ERR_BAD_DEVICE,
167 "no mapping exists for `%s'", name);
169 disk->has_partitions = 1;
170 disk->id = drive;
172 /* Get the size. */
173 #if defined(__MINGW32__)
175 grub_uint64_t size;
177 size = grub_util_get_disk_size (map[drive].device);
179 if (size % 512)
180 grub_util_error ("unaligned device size");
182 disk->total_sectors = size >> 9;
184 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
186 return GRUB_ERR_NONE;
188 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
190 unsigned long long nr;
191 int fd;
193 fd = open (map[drive].device, O_RDONLY);
194 if (fd == -1)
195 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
197 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
198 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
199 # else
200 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
201 # endif
203 close (fd);
204 goto fail;
207 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
208 if (ioctl (fd, DIOCGMEDIASIZE, &nr))
209 # else
210 if (ioctl (fd, BLKGETSIZE64, &nr))
211 # endif
213 close (fd);
214 goto fail;
217 close (fd);
218 disk->total_sectors = nr / 512;
220 if (nr % 512)
221 grub_util_error ("unaligned device size");
223 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
225 return GRUB_ERR_NONE;
228 fail:
229 /* In GNU/Hurd, stat() will return the right size. */
230 #elif !defined (__GNU__)
231 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
232 #endif
233 if (stat (map[drive].device, &st) < 0)
234 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
236 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
238 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
240 return GRUB_ERR_NONE;
243 #ifdef __linux__
244 static int
245 linux_find_partition (char *dev, unsigned long sector)
247 size_t len = strlen (dev);
248 const char *format;
249 char *p;
250 int i;
251 char real_dev[PATH_MAX];
253 strcpy(real_dev, dev);
255 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
257 p = real_dev + len - 4;
258 format = "part%d";
260 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
262 p = real_dev + len;
263 format = "p%d";
265 else
267 p = real_dev + len;
268 format = "%d";
271 for (i = 1; i < 10000; i++)
273 int fd;
274 struct hd_geometry hdg;
276 sprintf (p, format, i);
277 fd = open (real_dev, O_RDONLY);
278 if (fd == -1)
279 return 0;
281 if (ioctl (fd, HDIO_GETGEO, &hdg))
283 close (fd);
284 return 0;
287 close (fd);
289 if (hdg.start == sector)
291 strcpy (dev, real_dev);
292 return 1;
296 return 0;
298 #endif /* __linux__ */
300 static int
301 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
303 int fd;
305 #ifdef O_LARGEFILE
306 flags |= O_LARGEFILE;
307 #endif
308 #ifdef O_SYNC
309 flags |= O_SYNC;
310 #endif
311 #ifdef O_FSYNC
312 flags |= O_FSYNC;
313 #endif
314 #ifdef O_BINARY
315 flags |= O_BINARY;
316 #endif
318 #ifdef __linux__
319 /* Linux has a bug that the disk cache for a whole disk is not consistent
320 with the one for a partition of the disk. */
322 int is_partition = 0;
323 char dev[PATH_MAX];
325 strcpy (dev, map[disk->id].device);
326 if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0)
327 is_partition = linux_find_partition (dev, disk->partition->start);
329 /* Open the partition. */
330 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev);
331 fd = open (dev, flags);
332 if (fd < 0)
334 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
335 return -1;
338 /* Make the buffer cache consistent with the physical disk. */
339 ioctl (fd, BLKFLSBUF, 0);
341 if (is_partition)
342 sector -= disk->partition->start;
344 #else /* ! __linux__ */
345 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
346 int sysctl_flags, sysctl_oldflags;
347 const size_t sysctl_size = sizeof (sysctl_flags);
349 if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
351 grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
352 return -1;
354 sysctl_flags = sysctl_oldflags | 0x10;
355 if (! (sysctl_oldflags & 0x10)
356 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
358 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
359 return -1;
361 #endif
363 fd = open (map[disk->id].device, flags);
365 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
366 if (! (sysctl_oldflags & 0x10)
367 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
369 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
370 return -1;
372 #endif
374 if (fd < 0)
376 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
377 return -1;
379 #endif /* ! __linux__ */
381 #if defined(__linux__) && (!defined(__GLIBC__) || \
382 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
383 /* Maybe libc doesn't have large file support. */
385 loff_t offset, result;
386 static int _llseek (uint filedes, ulong hi, ulong lo,
387 loff_t *res, uint wh);
388 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
389 loff_t *, res, uint, wh);
391 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
392 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
394 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
395 close (fd);
396 return -1;
399 #else
401 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
403 if (lseek (fd, offset, SEEK_SET) != offset)
405 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
406 close (fd);
407 return -1;
410 #endif
412 return fd;
415 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
416 error occurs, otherwise return LEN. */
417 static ssize_t
418 nread (int fd, char *buf, size_t len)
420 ssize_t size = len;
422 while (len)
424 ssize_t ret = read (fd, buf, len);
426 if (ret <= 0)
428 if (errno == EINTR)
429 continue;
430 else
431 return ret;
434 len -= ret;
435 buf += ret;
438 return size;
441 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
442 error occurs, otherwise return LEN. */
443 static ssize_t
444 nwrite (int fd, const char *buf, size_t len)
446 ssize_t size = len;
448 while (len)
450 ssize_t ret = write (fd, buf, len);
452 if (ret <= 0)
454 if (errno == EINTR)
455 continue;
456 else
457 return ret;
460 len -= ret;
461 buf += ret;
464 return size;
467 static grub_err_t
468 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
469 grub_size_t size, char *buf)
471 int fd;
473 fd = open_device (disk, sector, O_RDONLY);
474 if (fd < 0)
475 return grub_errno;
477 #ifdef __linux__
478 if (sector == 0 && size > 1)
480 /* Work around a bug in Linux ez remapping. Linux remaps all
481 sectors that are read together with the MBR in one read. It
482 should only remap the MBR, so we split the read in two
483 parts. -jochen */
484 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
486 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
487 close (fd);
488 return grub_errno;
491 buf += GRUB_DISK_SECTOR_SIZE;
492 size--;
494 #endif /* __linux__ */
496 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
497 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
498 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
500 close (fd);
501 return grub_errno;
504 static grub_err_t
505 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
506 grub_size_t size, const char *buf)
508 int fd;
510 fd = open_device (disk, sector, O_WRONLY);
511 if (fd < 0)
512 return grub_errno;
514 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
515 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
516 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
518 close (fd);
519 return grub_errno;
522 static struct grub_disk_dev grub_util_biosdisk_dev =
524 .name = "biosdisk",
525 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
526 .iterate = grub_util_biosdisk_iterate,
527 .open = grub_util_biosdisk_open,
528 .close = 0,
529 .read = grub_util_biosdisk_read,
530 .write = grub_util_biosdisk_write,
531 .next = 0
534 static void
535 read_device_map (const char *dev_map)
537 FILE *fp;
538 char buf[1024]; /* XXX */
539 int lineno = 0;
540 struct stat st;
542 auto void show_error (const char *msg);
543 void show_error (const char *msg)
545 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
548 fp = fopen (dev_map, "r");
549 if (! fp)
550 grub_util_error ("Cannot open `%s'", dev_map);
552 while (fgets (buf, sizeof (buf), fp))
554 char *p = buf;
555 char *e;
556 int drive;
558 lineno++;
560 /* Skip leading spaces. */
561 while (*p && isspace (*p))
562 p++;
564 /* If the first character is `#' or NUL, skip this line. */
565 if (*p == '\0' || *p == '#')
566 continue;
568 if (*p != '(')
569 show_error ("No open parenthesis found");
571 p++;
572 /* Find a free slot. */
573 drive = find_free_slot ();
574 if (drive < 0)
575 show_error ("Map table size exceeded");
577 e = p;
578 p = strchr (p, ')');
579 if (! p)
580 show_error ("No close parenthesis found");
582 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
583 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
584 map[drive].drive[p - e] = '\0';
586 p++;
587 /* Skip leading spaces. */
588 while (*p && isspace (*p))
589 p++;
591 if (*p == '\0')
592 show_error ("No filename found");
594 /* NUL-terminate the filename. */
595 e = p;
596 while (*e && ! isspace (*e))
597 e++;
598 *e = '\0';
600 #ifdef __MINGW32__
601 (void) st;
602 if (grub_util_get_disk_size (p) == -1LL)
603 #else
604 if (stat (p, &st) == -1)
605 #endif
607 free (map[drive].drive);
608 map[drive].drive = NULL;
609 grub_util_info ("Cannot stat `%s', skipping", p);
610 continue;
613 #ifdef __linux__
614 /* On Linux, the devfs uses symbolic links horribly, and that
615 confuses the interface very much, so use realpath to expand
616 symbolic links. */
617 map[drive].device = xmalloc (PATH_MAX);
618 if (! realpath (p, map[drive].device))
619 grub_util_error ("Cannot get the real path of `%s'", p);
620 #else
621 map[drive].device = xstrdup (p);
622 #endif
625 fclose (fp);
628 void
629 grub_util_biosdisk_init (const char *dev_map)
631 read_device_map (dev_map);
632 grub_disk_dev_register (&grub_util_biosdisk_dev);
635 void
636 grub_util_biosdisk_fini (void)
638 unsigned i;
640 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
642 if (map[i].drive)
643 free (map[i].drive);
644 if (map[i].device)
645 free (map[i].device);
646 map[i].drive = map[i].device = NULL;
649 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
652 static char *
653 make_device_name (int drive, int dos_part, int bsd_part)
655 int len = strlen(map[drive].drive);
656 char *p;
658 if (dos_part >= 0)
660 /* Add in char length of dos_part+1 */
661 int tmp = dos_part + 1;
662 len++;
663 while ((tmp /= 10) != 0)
664 len++;
666 if (bsd_part >= 0)
667 len += 2;
669 /* Length to alloc is: char length of map[drive].drive, plus
670 * char length of (dos_part+1) or of bsd_part, plus
671 * 2 for the comma and a null/end of string (\0)
673 p = xmalloc (len + 2);
674 sprintf (p, "%s", map[drive].drive);
676 if (dos_part >= 0)
677 sprintf (p + strlen (p), ",%d", dos_part + 1);
679 if (bsd_part >= 0)
680 sprintf (p + strlen (p), ",%c", bsd_part + 'a');
682 return p;
685 static char *
686 convert_system_partition_to_system_disk (const char *os_dev)
688 #if defined(__linux__)
689 char *path = xmalloc (PATH_MAX);
690 if (! realpath (os_dev, path))
691 return 0;
693 if (strncmp ("/dev/", path, 5) == 0)
695 char *p = path + 5;
697 /* If this is an IDE disk. */
698 if (strncmp ("ide/", p, 4) == 0)
700 p = strstr (p, "part");
701 if (p)
702 strcpy (p, "disc");
704 return path;
707 /* If this is a SCSI disk. */
708 if (strncmp ("scsi/", p, 5) == 0)
710 p = strstr (p, "part");
711 if (p)
712 strcpy (p, "disc");
714 return path;
717 /* If this is a DAC960 disk. */
718 if (strncmp ("rd/c", p, 4) == 0)
720 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
721 p = strchr (p, 'p');
722 if (p)
723 *p = '\0';
725 return path;
728 /* If this is a CCISS disk. */
729 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
731 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
732 p = strchr (p, 'p');
733 if (p)
734 *p = '\0';
736 return path;
739 /* If this is a Compaq Intelligent Drive Array. */
740 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
742 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
743 p = strchr (p, 'p');
744 if (p)
745 *p = '\0';
747 return path;
750 /* If this is an I2O disk. */
751 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
753 /* /dev/i2o/hd[a-z]([0-9]+)? */
754 p[sizeof ("i2o/hda") - 1] = '\0';
755 return path;
758 /* If this is a MultiMediaCard (MMC). */
759 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
761 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
762 p = strchr (p, 'p');
763 if (p)
764 *p = '\0';
766 return path;
769 /* If this is an IDE, SCSI or Virtio disk. */
770 if (strncmp ("vdisk", p, 5) == 0
771 && p[5] >= 'a' && p[5] <= 'z')
773 /* /dev/vdisk[a-z][0-9]* */
774 p[6] = '\0';
775 return path;
777 if ((strncmp ("hd", p, 2) == 0
778 || strncmp ("vd", p, 2) == 0
779 || strncmp ("sd", p, 2) == 0)
780 && p[2] >= 'a' && p[2] <= 'z')
782 /* /dev/[hsv]d[a-z][0-9]* */
783 p[3] = '\0';
784 return path;
787 /* If this is a Xen virtual block device. */
788 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
790 /* /dev/xvd[a-z][0-9]* */
791 p[4] = '\0';
792 return path;
796 return path;
798 #elif defined(__GNU__)
799 char *path = xstrdup (os_dev);
800 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
802 char *p = strchr (path + 7, 's');
803 if (p)
804 *p = '\0';
806 return path;
808 #elif defined(__CYGWIN__)
809 char *path = xstrdup (os_dev);
810 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
811 path[8] = 0;
812 return path;
814 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
815 char *path = xstrdup (os_dev);
816 if (strncmp ("/dev/", path, 5) == 0)
818 char *p;
819 for (p = path + 5; *p; ++p)
820 if (grub_isdigit(*p))
822 p = strchr (p, 's');
823 if (p)
824 *p = '\0';
825 break;
828 return path;
830 #else
831 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
832 return xstrdup (os_dev);
833 #endif
836 static int
837 device_is_wholedisk (const char *os_dev)
839 int len = strlen (os_dev);
841 if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
842 return 1;
843 return 0;
846 static int
847 find_system_device (const char *os_dev)
849 int i;
850 char *os_disk;
852 os_disk = convert_system_partition_to_system_disk (os_dev);
853 if (! os_disk)
854 return -1;
856 for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
857 if (map[i].device && strcmp (map[i].device, os_disk) == 0)
859 free (os_disk);
860 return i;
863 free (os_disk);
864 return -1;
867 char *
868 grub_util_biosdisk_get_grub_dev (const char *os_dev)
870 struct stat st;
871 int drive;
873 if (stat (os_dev, &st) < 0)
875 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
876 return 0;
879 drive = find_system_device (os_dev);
880 if (drive < 0)
882 grub_error (GRUB_ERR_BAD_DEVICE,
883 "no mapping exists for `%s'", os_dev);
884 return 0;
887 if (grub_strcmp (os_dev, convert_system_partition_to_system_disk (os_dev))
888 == 0)
889 return make_device_name (drive, -1, -1);
891 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
892 if (! S_ISCHR (st.st_mode))
893 #else
894 if (! S_ISBLK (st.st_mode))
895 #endif
896 return make_device_name (drive, -1, -1);
898 #if defined(__linux__) || defined(__CYGWIN__)
899 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
900 partition, so mapping them to GRUB devices is not trivial.
901 Here, get the start sector of a partition by HDIO_GETGEO, and
902 compare it with each partition GRUB recognizes.
904 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
905 does not count the extended partition and missing primary
906 partitions. Use same method as on Linux here. */
908 char *name;
909 grub_disk_t disk;
910 int fd;
911 struct hd_geometry hdg;
912 int dos_part = -1;
913 int bsd_part = -1;
914 auto int find_partition (grub_disk_t disk,
915 const grub_partition_t partition);
917 int find_partition (grub_disk_t disk __attribute__ ((unused)),
918 const grub_partition_t partition)
920 struct grub_pc_partition *pcdata = NULL;
922 if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
923 pcdata = partition->data;
925 if (pcdata)
927 if (pcdata->bsd_part < 0)
928 grub_util_info ("DOS partition %d starts from %lu",
929 pcdata->dos_part, partition->start);
930 else
931 grub_util_info ("BSD partition %d,%c starts from %lu",
932 pcdata->dos_part, pcdata->bsd_part + 'a',
933 partition->start);
935 else
937 grub_util_info ("Partition %d starts from %lu",
938 partition->index, partition->start);
941 if (hdg.start == partition->start)
943 if (pcdata)
945 dos_part = pcdata->dos_part;
946 bsd_part = pcdata->bsd_part;
948 else
950 dos_part = partition->index;
951 bsd_part = -1;
953 return 1;
956 return 0;
959 name = make_device_name (drive, -1, -1);
961 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
962 return name;
964 fd = open (os_dev, O_RDONLY);
965 if (fd == -1)
967 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk geometry", os_dev);
968 free (name);
969 return 0;
972 if (ioctl (fd, HDIO_GETGEO, &hdg))
974 grub_error (GRUB_ERR_BAD_DEVICE,
975 "cannot get geometry of `%s'", os_dev);
976 close (fd);
977 free (name);
978 return 0;
981 close (fd);
983 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
985 if (hdg.start == 0 && device_is_wholedisk (os_dev))
986 return name;
988 grub_util_info ("opening the device %s", name);
989 disk = grub_disk_open (name);
990 free (name);
992 if (! disk)
993 return 0;
995 grub_partition_iterate (disk, find_partition);
996 if (grub_errno != GRUB_ERR_NONE)
998 grub_disk_close (disk);
999 return 0;
1002 if (dos_part < 0)
1004 grub_disk_close (disk);
1005 grub_error (GRUB_ERR_BAD_DEVICE,
1006 "cannot find the partition of `%s'", os_dev);
1007 return 0;
1010 return make_device_name (drive, dos_part, bsd_part);
1013 #elif defined(__GNU__)
1014 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
1016 char *p;
1017 int dos_part = -1;
1018 int bsd_part = -1;
1020 p = strrchr (os_dev, 's');
1021 if (p)
1023 long int n;
1024 char *q;
1026 p++;
1027 n = strtol (p, &q, 10);
1028 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1030 dos_part = (int) n;
1032 if (*q >= 'a' && *q <= 'g')
1033 bsd_part = *q - 'a';
1037 return make_device_name (drive, dos_part, bsd_part);
1040 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1041 /* FreeBSD uses "/dev/[a-z]+[0-9]+(s[0-9]+[a-z]?)?". */
1043 int dos_part = -1;
1044 int bsd_part = -1;
1046 if (strncmp ("/dev/", os_dev, 5) == 0)
1048 char *p, *q;
1049 long int n;
1051 for (p = os_dev + 5; *p; ++p)
1052 if (grub_isdigit(*p))
1054 p = strchr (p, 's');
1055 if (p)
1057 p++;
1058 n = strtol (p, &q, 10);
1059 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1061 dos_part = (int) n - 1;
1063 if (*q >= 'a' && *q <= 'g')
1064 bsd_part = *q - 'a';
1067 break;
1071 return make_device_name (drive, dos_part, bsd_part);
1074 #else
1075 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1076 return make_device_name (drive, -1, -1);
1077 #endif