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 get_drive (const char *name
)
119 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
120 if (! strcmp (map
[i
].drive
, name
))
125 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
134 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
138 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
139 if (map
[i
].drive
&& hook (map
[i
].drive
))
146 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
151 drive
= get_drive (name
);
153 return grub_error (GRUB_ERR_BAD_DEVICE
,
154 "no mapping exists for `%s'", name
);
156 disk
->has_partitions
= 1;
160 #if defined(__linux__) || defined(__CYGWIN__)
162 unsigned long long nr
;
165 fd
= open (map
[drive
].device
, O_RDONLY
);
167 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", map
[drive
].device
);
169 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
175 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
182 disk
->total_sectors
= nr
/ 512;
185 grub_util_error ("unaligned device size");
187 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
189 return GRUB_ERR_NONE
;
193 /* In GNU/Hurd, stat() will return the right size. */
194 #elif !defined (__GNU__)
195 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
197 if (stat (map
[drive
].device
, &st
) < 0)
198 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", map
[drive
].device
);
200 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
202 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
204 return GRUB_ERR_NONE
;
209 linux_find_partition (char *dev
, unsigned long sector
)
211 size_t len
= strlen (dev
);
215 char real_dev
[PATH_MAX
];
217 strcpy(real_dev
, dev
);
219 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
221 p
= real_dev
+ len
- 4;
224 else if (real_dev
[len
- 1] >= '0' && real_dev
[len
- 1] <= '9')
235 for (i
= 1; i
< 10000; i
++)
238 struct hd_geometry hdg
;
240 sprintf (p
, format
, i
);
241 fd
= open (real_dev
, O_RDONLY
);
245 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
253 if (hdg
.start
== sector
)
255 strcpy (dev
, real_dev
);
262 #endif /* __linux__ */
265 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
270 flags
|= O_LARGEFILE
;
280 /* Linux has a bug that the disk cache for a whole disk is not consistent
281 with the one for a partition of the disk. */
283 int is_partition
= 0;
286 strcpy (dev
, map
[disk
->id
].device
);
287 if (disk
->partition
&& strncmp (map
[disk
->id
].device
, "/dev/", 5) == 0)
288 is_partition
= linux_find_partition (dev
, disk
->partition
->start
);
290 /* Open the partition. */
291 grub_util_info ("opening the device `%s'", dev
);
292 fd
= open (dev
, flags
);
295 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
299 /* Make the buffer cache consistent with the physical disk. */
300 ioctl (fd
, BLKFLSBUF
, 0);
303 sector
-= disk
->partition
->start
;
305 #else /* ! __linux__ */
306 fd
= open (map
[disk
->id
].device
, flags
);
309 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", map
[disk
->id
].device
);
312 #endif /* ! __linux__ */
314 #if defined(__linux__) && (!defined(__GLIBC__) || \
315 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
316 /* Maybe libc doesn't have large file support. */
318 loff_t offset
, result
;
319 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
320 loff_t
*res
, uint wh
);
321 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
322 loff_t
*, res
, uint
, wh
);
324 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
325 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
327 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
334 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
336 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
338 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
348 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
349 error occurs, otherwise return LEN. */
351 nread (int fd
, char *buf
, size_t len
)
357 ssize_t ret
= read (fd
, buf
, len
);
374 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
375 error occurs, otherwise return LEN. */
377 nwrite (int fd
, const char *buf
, size_t len
)
383 ssize_t ret
= write (fd
, buf
, len
);
401 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
402 grub_size_t size
, char *buf
)
406 fd
= open_device (disk
, sector
, O_RDONLY
);
411 if (sector
== 0 && size
> 1)
413 /* Work around a bug in Linux ez remapping. Linux remaps all
414 sectors that are read together with the MBR in one read. It
415 should only remap the MBR, so we split the read in two
417 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
419 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
].device
);
424 buf
+= GRUB_DISK_SECTOR_SIZE
;
427 #endif /* __linux__ */
429 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
430 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
431 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
].device
);
438 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
439 grub_size_t size
, const char *buf
)
443 fd
= open_device (disk
, sector
, O_WRONLY
);
447 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
448 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
449 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
].device
);
455 static struct grub_disk_dev grub_util_biosdisk_dev
=
458 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
459 .iterate
= grub_util_biosdisk_iterate
,
460 .open
= grub_util_biosdisk_open
,
462 .read
= grub_util_biosdisk_read
,
463 .write
= grub_util_biosdisk_write
,
468 read_device_map (const char *dev_map
)
471 char buf
[1024]; /* XXX */
475 auto void show_error (const char *msg
);
476 void show_error (const char *msg
)
478 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
481 fp
= fopen (dev_map
, "r");
483 grub_util_error ("Cannot open `%s'", dev_map
);
485 while (fgets (buf
, sizeof (buf
), fp
))
493 /* Skip leading spaces. */
494 while (*p
&& isspace (*p
))
497 /* If the first character is `#' or NUL, skip this line. */
498 if (*p
== '\0' || *p
== '#')
502 show_error ("No open parenthesis found");
505 /* Find a free slot. */
506 drive
= get_drive (NULL
);
508 show_error ("Map table size exceeded");
513 show_error ("No close parenthesis found");
515 map
[drive
].drive
= xmalloc (p
- e
+ sizeof ('\0'));
516 strncpy (map
[drive
].drive
, e
, p
- e
+ sizeof ('\0'));
517 map
[drive
].drive
[p
- e
] = '\0';
520 /* Skip leading spaces. */
521 while (*p
&& isspace (*p
))
525 show_error ("No filename found");
527 /* NUL-terminate the filename. */
529 while (*e
&& ! isspace (*e
))
533 if (stat (p
, &st
) == -1)
535 grub_util_info ("Cannot stat `%s', skipping", p
);
540 /* On Linux, the devfs uses symbolic links horribly, and that
541 confuses the interface very much, so use realpath to expand
543 map
[drive
].device
= xmalloc (PATH_MAX
);
544 if (! realpath (p
, map
[drive
].device
))
545 grub_util_error ("Cannot get the real path of `%s'", p
);
547 map
[drive
].device
= xstrdup (p
);
555 grub_util_biosdisk_init (const char *dev_map
)
557 read_device_map (dev_map
);
558 grub_disk_dev_register (&grub_util_biosdisk_dev
);
562 grub_util_biosdisk_fini (void)
566 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
571 free (map
[i
].device
);
572 map
[i
].drive
= map
[i
].device
= NULL
;
575 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
579 make_device_name (int drive
, int dos_part
, int bsd_part
)
584 sprintf (p
, "%s", map
[drive
].drive
);
587 sprintf (p
+ strlen (p
), ",%d", dos_part
+ 1);
590 sprintf (p
+ strlen (p
), ",%c", bsd_part
+ 'a');
596 get_os_disk (const char *os_dev
)
598 #if defined(__linux__)
599 char *path
= xmalloc (PATH_MAX
);
600 if (! realpath (os_dev
, path
))
603 if (strncmp ("/dev/", path
, 5) == 0)
607 /* If this is an IDE disk. */
608 if (strncmp ("ide/", p
, 4) == 0)
610 p
= strstr (p
, "part");
617 /* If this is a SCSI disk. */
618 if (strncmp ("scsi/", p
, 5) == 0)
620 p
= strstr (p
, "part");
627 /* If this is a DAC960 disk. */
628 if (strncmp ("rd/c", p
, 4) == 0)
630 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
638 /* If this is a CCISS disk. */
639 if (strncmp ("cciss/c", p
, sizeof ("cciss/c") - 1) == 0)
641 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
649 /* If this is a MultiMediaCard (MMC). */
650 if (strncmp ("mmcblk", p
, sizeof ("mmcblk") - 1) == 0)
652 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
660 /* If this is an IDE, SCSI or Virtio disk. */
661 if ((strncmp ("hd", p
, 2) == 0
662 || strncmp ("vd", p
, 2) == 0
663 || strncmp ("sd", p
, 2) == 0)
664 && p
[2] >= 'a' && p
[2] <= 'z')
666 /* /dev/[hsv]d[a-z][0-9]* */
671 /* If this is a Xen virtual block device. */
672 if ((strncmp ("xvd", p
, 3) == 0) && p
[3] >= 'a' && p
[3] <= 'z')
674 /* /dev/xvd[a-z][0-9]* */
682 #elif defined(__GNU__)
683 char *path
= xstrdup (os_dev
);
684 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
686 char *p
= strchr (path
+ 7, 's');
692 #elif defined(__CYGWIN__)
693 char *path
= xstrdup (os_dev
);
694 if (strncmp ("/dev/sd", path
, 7) == 0 && 'a' <= path
[7] && path
[7] <= 'z')
699 # warning "The function `get_os_disk' might not work on your OS correctly."
700 return xstrdup (os_dev
);
705 find_drive (const char *os_dev
)
710 os_disk
= get_os_disk (os_dev
);
714 for (i
= 0; i
< (int) (sizeof (map
) / sizeof (map
[0])); i
++)
715 if (map
[i
].device
&& strcmp (map
[i
].device
, os_disk
) == 0)
726 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
731 if (stat (os_dev
, &st
) < 0)
733 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
737 drive
= find_drive (os_dev
);
740 grub_error (GRUB_ERR_BAD_DEVICE
,
741 "no mapping exists for `%s'", os_dev
);
745 if (! S_ISBLK (st
.st_mode
))
746 return make_device_name (drive
, -1, -1);
748 #if defined(__linux__) || defined(__CYGWIN__)
749 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
750 partition, so mapping them to GRUB devices is not trivial.
751 Here, get the start sector of a partition by HDIO_GETGEO, and
752 compare it with each partition GRUB recognizes.
754 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
755 does not count the extended partition and missing primary
756 partitions. Use same method as on Linux here. */
761 struct hd_geometry hdg
;
764 auto int find_partition (grub_disk_t disk
,
765 const grub_partition_t partition
);
767 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
768 const grub_partition_t partition
)
770 struct grub_pc_partition
*pcdata
= NULL
;
772 if (strcmp (partition
->partmap
->name
, "pc_partition_map") == 0)
773 pcdata
= partition
->data
;
777 if (pcdata
->bsd_part
< 0)
778 grub_util_info ("DOS partition %d starts from %lu",
779 pcdata
->dos_part
, partition
->start
);
781 grub_util_info ("BSD partition %d,%c starts from %lu",
782 pcdata
->dos_part
, pcdata
->bsd_part
+ 'a',
787 grub_util_info ("Partition %d starts from %lu",
788 partition
->index
, partition
->start
);
791 if (hdg
.start
== partition
->start
)
795 dos_part
= pcdata
->dos_part
;
796 bsd_part
= pcdata
->bsd_part
;
800 dos_part
= partition
->index
;
809 name
= make_device_name (drive
, -1, -1);
811 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
814 fd
= open (os_dev
, O_RDONLY
);
817 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", os_dev
);
822 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
824 grub_error (GRUB_ERR_BAD_DEVICE
,
825 "cannot get geometry of `%s'", os_dev
);
833 grub_util_info ("%s starts from %lu", os_dev
, hdg
.start
);
838 grub_util_info ("opening the device %s", name
);
839 disk
= grub_disk_open (name
);
845 if (grub_partition_iterate (disk
, find_partition
) != GRUB_ERR_NONE
)
847 grub_disk_close (disk
);
853 grub_disk_close (disk
);
854 grub_error (GRUB_ERR_BAD_DEVICE
,
855 "cannot find the partition of `%s'", os_dev
);
859 return make_device_name (drive
, dos_part
, bsd_part
);
862 #elif defined(__GNU__)
863 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
869 p
= strrchr (os_dev
, 's');
876 n
= strtol (p
, &q
, 10);
877 if (p
!= q
&& n
!= LONG_MIN
&& n
!= LONG_MAX
)
881 if (*q
>= 'a' && *q
<= 'g')
886 return make_device_name (drive
, dos_part
, bsd_part
);
890 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
891 return make_device_name (drive
, -1, -1);