1 /* biosdisk.c - emulate biosdisk */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008 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>
25 #include <grub/util/misc.h>
26 #include <grub/util/biosdisk.h>
34 #include <sys/types.h>
41 # include <sys/ioctl.h> /* ioctl */
42 # if !defined(__GLIBC__) || \
43 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
44 /* Maybe libc doesn't have large file support. */
45 # include <linux/unistd.h> /* _llseek */
46 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
48 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
49 # endif /* ! BLKFLSBUF */
50 # include <sys/ioctl.h> /* ioctl */
52 # define HDIO_GETGEO 0x0301 /* get device geometry */
53 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
58 unsigned char sectors
;
59 unsigned short cylinders
;
62 # endif /* ! HDIO_GETGEO */
64 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
65 # endif /* ! BLKGETSIZE64 */
69 # endif /* ! MINORBITS */
70 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
73 # define FLOPPY_MAJOR 2
74 # endif /* ! FLOPPY_MAJOR */
77 # endif /* ! LOOP_MAJOR */
78 #endif /* __linux__ */
81 # include <sys/ioctl.h>
82 # include <cygwin/fs.h> /* BLKGETSIZE64 */
83 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
84 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
85 # define FLOPPY_MAJOR 2
95 /* Check if we have devfs support. */
99 static int dev_devfsd_exists
= -1;
101 if (dev_devfsd_exists
< 0)
105 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
108 return dev_devfsd_exists
;
110 #endif /* __linux__ */
113 find_grub_drive (const char *name
)
119 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
120 if (map
[i
].drive
&& ! strcmp (map
[i
].drive
, name
))
132 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
140 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
144 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
145 if (map
[i
].drive
&& hook (map
[i
].drive
))
152 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
157 drive
= find_grub_drive (name
);
159 return grub_error (GRUB_ERR_BAD_DEVICE
,
160 "no mapping exists for `%s'", name
);
162 disk
->has_partitions
= 1;
166 #if defined(__MINGW32__)
170 size
= grub_util_get_disk_size (map
[drive
].device
);
173 grub_util_error ("unaligned device size");
175 disk
->total_sectors
= size
>> 9;
177 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
179 return GRUB_ERR_NONE
;
181 #elif defined(__linux__) || defined(__CYGWIN__)
183 unsigned long long nr
;
186 fd
= open (map
[drive
].device
, O_RDONLY
);
188 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk size", map
[drive
].device
);
190 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
196 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
203 disk
->total_sectors
= nr
/ 512;
206 grub_util_error ("unaligned device size");
208 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
210 return GRUB_ERR_NONE
;
214 /* In GNU/Hurd, stat() will return the right size. */
215 #elif !defined (__GNU__)
216 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
218 if (stat (map
[drive
].device
, &st
) < 0)
219 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", map
[drive
].device
);
221 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
223 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
225 return GRUB_ERR_NONE
;
230 linux_find_partition (char *dev
, unsigned long sector
)
232 size_t len
= strlen (dev
);
236 char real_dev
[PATH_MAX
];
238 strcpy(real_dev
, dev
);
240 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
242 p
= real_dev
+ len
- 4;
245 else if (real_dev
[len
- 1] >= '0' && real_dev
[len
- 1] <= '9')
256 for (i
= 1; i
< 10000; i
++)
259 struct hd_geometry hdg
;
261 sprintf (p
, format
, i
);
262 fd
= open (real_dev
, O_RDONLY
);
266 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
274 if (hdg
.start
== sector
)
276 strcpy (dev
, real_dev
);
283 #endif /* __linux__ */
286 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
291 flags
|= O_LARGEFILE
;
304 /* Linux has a bug that the disk cache for a whole disk is not consistent
305 with the one for a partition of the disk. */
307 int is_partition
= 0;
310 strcpy (dev
, map
[disk
->id
].device
);
311 if (disk
->partition
&& strncmp (map
[disk
->id
].device
, "/dev/", 5) == 0)
312 is_partition
= linux_find_partition (dev
, disk
->partition
->start
);
314 /* Open the partition. */
315 grub_util_info ("opening the device `%s' in open_device()", dev
);
316 fd
= open (dev
, flags
);
319 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
323 /* Make the buffer cache consistent with the physical disk. */
324 ioctl (fd
, BLKFLSBUF
, 0);
327 sector
-= disk
->partition
->start
;
329 #else /* ! __linux__ */
330 fd
= open (map
[disk
->id
].device
, flags
);
333 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' in open_device()", map
[disk
->id
].device
);
336 #endif /* ! __linux__ */
338 #if defined(__linux__) && (!defined(__GLIBC__) || \
339 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
340 /* Maybe libc doesn't have large file support. */
342 loff_t offset
, result
;
343 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
344 loff_t
*res
, uint wh
);
345 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
346 loff_t
*, res
, uint
, wh
);
348 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
349 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
351 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
358 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
360 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
362 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
372 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
373 error occurs, otherwise return LEN. */
375 nread (int fd
, char *buf
, size_t len
)
381 ssize_t ret
= read (fd
, buf
, len
);
398 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
399 error occurs, otherwise return LEN. */
401 nwrite (int fd
, const char *buf
, size_t len
)
407 ssize_t ret
= write (fd
, buf
, len
);
425 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
426 grub_size_t size
, char *buf
)
430 fd
= open_device (disk
, sector
, O_RDONLY
);
435 if (sector
== 0 && size
> 1)
437 /* Work around a bug in Linux ez remapping. Linux remaps all
438 sectors that are read together with the MBR in one read. It
439 should only remap the MBR, so we split the read in two
441 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
443 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
].device
);
448 buf
+= GRUB_DISK_SECTOR_SIZE
;
451 #endif /* __linux__ */
453 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
454 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
455 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
].device
);
462 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
463 grub_size_t size
, const char *buf
)
467 fd
= open_device (disk
, sector
, O_WRONLY
);
471 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
472 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
473 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
].device
);
479 static struct grub_disk_dev grub_util_biosdisk_dev
=
482 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
483 .iterate
= grub_util_biosdisk_iterate
,
484 .open
= grub_util_biosdisk_open
,
486 .read
= grub_util_biosdisk_read
,
487 .write
= grub_util_biosdisk_write
,
492 read_device_map (const char *dev_map
)
495 char buf
[1024]; /* XXX */
499 auto void show_error (const char *msg
);
500 void show_error (const char *msg
)
502 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
505 fp
= fopen (dev_map
, "r");
507 grub_util_error ("Cannot open `%s'", dev_map
);
509 while (fgets (buf
, sizeof (buf
), fp
))
517 /* Skip leading spaces. */
518 while (*p
&& isspace (*p
))
521 /* If the first character is `#' or NUL, skip this line. */
522 if (*p
== '\0' || *p
== '#')
526 show_error ("No open parenthesis found");
529 /* Find a free slot. */
530 drive
= find_free_slot ();
532 show_error ("Map table size exceeded");
537 show_error ("No close parenthesis found");
539 map
[drive
].drive
= xmalloc (p
- e
+ sizeof ('\0'));
540 strncpy (map
[drive
].drive
, e
, p
- e
+ sizeof ('\0'));
541 map
[drive
].drive
[p
- e
] = '\0';
544 /* Skip leading spaces. */
545 while (*p
&& isspace (*p
))
549 show_error ("No filename found");
551 /* NUL-terminate the filename. */
553 while (*e
&& ! isspace (*e
))
557 if (stat (p
, &st
) == -1)
559 free (map
[drive
].drive
);
560 map
[drive
].drive
= NULL
;
561 grub_util_info ("Cannot stat `%s', skipping", p
);
566 /* On Linux, the devfs uses symbolic links horribly, and that
567 confuses the interface very much, so use realpath to expand
569 map
[drive
].device
= xmalloc (PATH_MAX
);
570 if (! realpath (p
, map
[drive
].device
))
571 grub_util_error ("Cannot get the real path of `%s'", p
);
573 map
[drive
].device
= xstrdup (p
);
581 grub_util_biosdisk_init (const char *dev_map
)
583 read_device_map (dev_map
);
584 grub_disk_dev_register (&grub_util_biosdisk_dev
);
588 grub_util_biosdisk_fini (void)
592 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
597 free (map
[i
].device
);
598 map
[i
].drive
= map
[i
].device
= NULL
;
601 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
605 make_device_name (int drive
, int dos_part
, int bsd_part
)
610 sprintf (p
, "%s", map
[drive
].drive
);
613 sprintf (p
+ strlen (p
), ",%d", dos_part
+ 1);
616 sprintf (p
+ strlen (p
), ",%c", bsd_part
+ 'a');
622 convert_system_partition_to_system_disk (const char *os_dev
)
624 #if defined(__linux__)
625 char *path
= xmalloc (PATH_MAX
);
626 if (! realpath (os_dev
, path
))
629 if (strncmp ("/dev/", path
, 5) == 0)
633 /* If this is an IDE disk. */
634 if (strncmp ("ide/", p
, 4) == 0)
636 p
= strstr (p
, "part");
643 /* If this is a SCSI disk. */
644 if (strncmp ("scsi/", p
, 5) == 0)
646 p
= strstr (p
, "part");
653 /* If this is a DAC960 disk. */
654 if (strncmp ("rd/c", p
, 4) == 0)
656 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
664 /* If this is a CCISS disk. */
665 if (strncmp ("cciss/c", p
, sizeof ("cciss/c") - 1) == 0)
667 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
675 /* If this is a Compaq Intelligent Drive Array. */
676 if (strncmp ("ida/c", p
, sizeof ("ida/c") - 1) == 0)
678 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
686 /* If this is an I2O disk. */
687 if (strncmp ("i2o/hd", p
, sizeof ("i2o/hd") - 1) == 0)
689 /* /dev/i2o/hd[a-z]([0-9]+)? */
690 p
[sizeof ("i2o/hda") - 1] = '\0';
694 /* If this is a MultiMediaCard (MMC). */
695 if (strncmp ("mmcblk", p
, sizeof ("mmcblk") - 1) == 0)
697 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
705 /* If this is an IDE, SCSI or Virtio disk. */
706 if ((strncmp ("hd", p
, 2) == 0
707 || strncmp ("vd", p
, 2) == 0
708 || strncmp ("sd", p
, 2) == 0)
709 && p
[2] >= 'a' && p
[2] <= 'z')
711 /* /dev/[hsv]d[a-z][0-9]* */
716 /* If this is a Xen virtual block device. */
717 if ((strncmp ("xvd", p
, 3) == 0) && p
[3] >= 'a' && p
[3] <= 'z')
719 /* /dev/xvd[a-z][0-9]* */
727 #elif defined(__GNU__)
728 char *path
= xstrdup (os_dev
);
729 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
731 char *p
= strchr (path
+ 7, 's');
737 #elif defined(__CYGWIN__)
738 char *path
= xstrdup (os_dev
);
739 if (strncmp ("/dev/sd", path
, 7) == 0 && 'a' <= path
[7] && path
[7] <= 'z')
744 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
745 return xstrdup (os_dev
);
750 find_system_device (const char *os_dev
)
755 os_disk
= convert_system_partition_to_system_disk (os_dev
);
759 for (i
= 0; i
< (int) (sizeof (map
) / sizeof (map
[0])); i
++)
760 if (map
[i
].device
&& strcmp (map
[i
].device
, os_disk
) == 0)
771 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
776 if (stat (os_dev
, &st
) < 0)
778 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
782 drive
= find_system_device (os_dev
);
785 grub_error (GRUB_ERR_BAD_DEVICE
,
786 "no mapping exists for `%s'", os_dev
);
790 if (! S_ISBLK (st
.st_mode
))
791 return make_device_name (drive
, -1, -1);
793 #if defined(__linux__) || defined(__CYGWIN__)
794 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
795 partition, so mapping them to GRUB devices is not trivial.
796 Here, get the start sector of a partition by HDIO_GETGEO, and
797 compare it with each partition GRUB recognizes.
799 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
800 does not count the extended partition and missing primary
801 partitions. Use same method as on Linux here. */
806 struct hd_geometry hdg
;
809 auto int find_partition (grub_disk_t disk
,
810 const grub_partition_t partition
);
812 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
813 const grub_partition_t partition
)
815 struct grub_pc_partition
*pcdata
= NULL
;
817 if (strcmp (partition
->partmap
->name
, "pc_partition_map") == 0)
818 pcdata
= partition
->data
;
822 if (pcdata
->bsd_part
< 0)
823 grub_util_info ("DOS partition %d starts from %lu",
824 pcdata
->dos_part
, partition
->start
);
826 grub_util_info ("BSD partition %d,%c starts from %lu",
827 pcdata
->dos_part
, pcdata
->bsd_part
+ 'a',
832 grub_util_info ("Partition %d starts from %lu",
833 partition
->index
, partition
->start
);
836 if (hdg
.start
== partition
->start
)
840 dos_part
= pcdata
->dos_part
;
841 bsd_part
= pcdata
->bsd_part
;
845 dos_part
= partition
->index
;
854 name
= make_device_name (drive
, -1, -1);
856 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
859 fd
= open (os_dev
, O_RDONLY
);
862 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk geometry", os_dev
);
867 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
869 grub_error (GRUB_ERR_BAD_DEVICE
,
870 "cannot get geometry of `%s'", os_dev
);
878 grub_util_info ("%s starts from %lu", os_dev
, hdg
.start
);
883 grub_util_info ("opening the device %s", name
);
884 disk
= grub_disk_open (name
);
890 grub_partition_iterate (disk
, find_partition
);
891 if (grub_errno
!= GRUB_ERR_NONE
)
893 grub_disk_close (disk
);
899 grub_disk_close (disk
);
900 grub_error (GRUB_ERR_BAD_DEVICE
,
901 "cannot find the partition of `%s'", os_dev
);
905 return make_device_name (drive
, dos_part
, bsd_part
);
908 #elif defined(__GNU__)
909 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
915 p
= strrchr (os_dev
, 's');
922 n
= strtol (p
, &q
, 10);
923 if (p
!= q
&& n
!= LONG_MIN
&& n
!= LONG_MAX
)
927 if (*q
>= 'a' && *q
<= 'g')
932 return make_device_name (drive
, dos_part
, bsd_part
);
936 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
937 return make_device_name (drive
, -1, -1);