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/hostdisk.h>
27 #include <grub/misc.h>
35 #include <sys/types.h>
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)) */
49 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
50 # endif /* ! BLKFLSBUF */
51 # include <sys/ioctl.h> /* ioctl */
53 # define HDIO_GETGEO 0x0301 /* get device geometry */
54 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
59 unsigned char sectors
;
60 unsigned short cylinders
;
63 # endif /* ! HDIO_GETGEO */
65 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
66 # endif /* ! BLKGETSIZE64 */
70 # endif /* ! MINORBITS */
71 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
74 # define FLOPPY_MAJOR 2
75 # endif /* ! FLOPPY_MAJOR */
78 # endif /* ! LOOP_MAJOR */
79 #endif /* __linux__ */
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
96 /* Check if we have devfs support. */
100 static int dev_devfsd_exists
= -1;
102 if (dev_devfsd_exists
< 0)
106 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
109 return dev_devfsd_exists
;
111 #endif /* __linux__ */
114 find_grub_drive (const char *name
)
120 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
121 if (map
[i
].drive
&& ! strcmp (map
[i
].drive
, name
))
133 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
141 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
145 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
146 if (map
[i
].drive
&& hook (map
[i
].drive
))
153 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
158 drive
= find_grub_drive (name
);
160 return grub_error (GRUB_ERR_BAD_DEVICE
,
161 "no mapping exists for `%s'", name
);
163 disk
->has_partitions
= 1;
167 #if defined(__MINGW32__)
171 size
= grub_util_get_disk_size (map
[drive
].device
);
174 grub_util_error ("unaligned device size");
176 disk
->total_sectors
= size
>> 9;
178 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
180 return GRUB_ERR_NONE
;
182 #elif defined(__linux__) || defined(__CYGWIN__)
184 unsigned long long nr
;
187 fd
= open (map
[drive
].device
, O_RDONLY
);
189 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk size", map
[drive
].device
);
191 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
197 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
204 disk
->total_sectors
= nr
/ 512;
207 grub_util_error ("unaligned device size");
209 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
211 return GRUB_ERR_NONE
;
215 /* In GNU/Hurd, stat() will return the right size. */
216 #elif !defined (__GNU__)
217 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
219 if (stat (map
[drive
].device
, &st
) < 0)
220 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", map
[drive
].device
);
222 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
224 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
226 return GRUB_ERR_NONE
;
231 linux_find_partition (char *dev
, unsigned long sector
)
233 size_t len
= strlen (dev
);
237 char real_dev
[PATH_MAX
];
239 strcpy(real_dev
, dev
);
241 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
243 p
= real_dev
+ len
- 4;
246 else if (real_dev
[len
- 1] >= '0' && real_dev
[len
- 1] <= '9')
257 for (i
= 1; i
< 10000; i
++)
260 struct hd_geometry hdg
;
262 sprintf (p
, format
, i
);
263 fd
= open (real_dev
, O_RDONLY
);
267 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
275 if (hdg
.start
== sector
)
277 strcpy (dev
, real_dev
);
284 #endif /* __linux__ */
287 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
292 flags
|= O_LARGEFILE
;
305 /* Linux has a bug that the disk cache for a whole disk is not consistent
306 with the one for a partition of the disk. */
308 int is_partition
= 0;
311 strcpy (dev
, map
[disk
->id
].device
);
312 if (disk
->partition
&& strncmp (map
[disk
->id
].device
, "/dev/", 5) == 0)
313 is_partition
= linux_find_partition (dev
, disk
->partition
->start
);
315 /* Open the partition. */
316 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev
);
317 fd
= open (dev
, flags
);
320 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
324 /* Make the buffer cache consistent with the physical disk. */
325 ioctl (fd
, BLKFLSBUF
, 0);
328 sector
-= disk
->partition
->start
;
330 #else /* ! __linux__ */
331 fd
= open (map
[disk
->id
].device
, flags
);
334 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' in open_device()", map
[disk
->id
].device
);
337 #endif /* ! __linux__ */
339 #if defined(__linux__) && (!defined(__GLIBC__) || \
340 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
341 /* Maybe libc doesn't have large file support. */
343 loff_t offset
, result
;
344 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
345 loff_t
*res
, uint wh
);
346 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
347 loff_t
*, res
, uint
, wh
);
349 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
350 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
352 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
359 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
361 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
363 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
373 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
374 error occurs, otherwise return LEN. */
376 nread (int fd
, char *buf
, size_t len
)
382 ssize_t ret
= read (fd
, buf
, len
);
399 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
400 error occurs, otherwise return LEN. */
402 nwrite (int fd
, const char *buf
, size_t len
)
408 ssize_t ret
= write (fd
, buf
, len
);
426 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
427 grub_size_t size
, char *buf
)
431 fd
= open_device (disk
, sector
, O_RDONLY
);
436 if (sector
== 0 && size
> 1)
438 /* Work around a bug in Linux ez remapping. Linux remaps all
439 sectors that are read together with the MBR in one read. It
440 should only remap the MBR, so we split the read in two
442 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
444 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
].device
);
449 buf
+= GRUB_DISK_SECTOR_SIZE
;
452 #endif /* __linux__ */
454 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
455 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
456 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
].device
);
463 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
464 grub_size_t size
, const char *buf
)
468 fd
= open_device (disk
, sector
, O_WRONLY
);
472 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
473 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
474 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
].device
);
480 static struct grub_disk_dev grub_util_biosdisk_dev
=
483 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
484 .iterate
= grub_util_biosdisk_iterate
,
485 .open
= grub_util_biosdisk_open
,
487 .read
= grub_util_biosdisk_read
,
488 .write
= grub_util_biosdisk_write
,
493 read_device_map (const char *dev_map
)
496 char buf
[1024]; /* XXX */
500 auto void show_error (const char *msg
);
501 void show_error (const char *msg
)
503 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
506 fp
= fopen (dev_map
, "r");
508 grub_util_error ("Cannot open `%s'", dev_map
);
510 while (fgets (buf
, sizeof (buf
), fp
))
518 /* Skip leading spaces. */
519 while (*p
&& isspace (*p
))
522 /* If the first character is `#' or NUL, skip this line. */
523 if (*p
== '\0' || *p
== '#')
527 show_error ("No open parenthesis found");
530 /* Find a free slot. */
531 drive
= find_free_slot ();
533 show_error ("Map table size exceeded");
538 show_error ("No close parenthesis found");
540 map
[drive
].drive
= xmalloc (p
- e
+ sizeof ('\0'));
541 strncpy (map
[drive
].drive
, e
, p
- e
+ sizeof ('\0'));
542 map
[drive
].drive
[p
- e
] = '\0';
545 /* Skip leading spaces. */
546 while (*p
&& isspace (*p
))
550 show_error ("No filename found");
552 /* NUL-terminate the filename. */
554 while (*e
&& ! isspace (*e
))
558 if (stat (p
, &st
) == -1)
560 free (map
[drive
].drive
);
561 map
[drive
].drive
= NULL
;
562 grub_util_info ("Cannot stat `%s', skipping", p
);
567 /* On Linux, the devfs uses symbolic links horribly, and that
568 confuses the interface very much, so use realpath to expand
570 map
[drive
].device
= xmalloc (PATH_MAX
);
571 if (! realpath (p
, map
[drive
].device
))
572 grub_util_error ("Cannot get the real path of `%s'", p
);
574 map
[drive
].device
= xstrdup (p
);
582 grub_util_biosdisk_init (const char *dev_map
)
584 read_device_map (dev_map
);
585 grub_disk_dev_register (&grub_util_biosdisk_dev
);
589 grub_util_biosdisk_fini (void)
593 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
598 free (map
[i
].device
);
599 map
[i
].drive
= map
[i
].device
= NULL
;
602 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
606 make_device_name (int drive
, int dos_part
, int bsd_part
)
611 sprintf (p
, "%s", map
[drive
].drive
);
614 sprintf (p
+ strlen (p
), ",%d", dos_part
+ 1);
617 sprintf (p
+ strlen (p
), ",%c", bsd_part
+ 'a');
623 convert_system_partition_to_system_disk (const char *os_dev
)
625 #if defined(__linux__)
626 char *path
= xmalloc (PATH_MAX
);
627 if (! realpath (os_dev
, path
))
630 if (strncmp ("/dev/", path
, 5) == 0)
634 /* If this is an IDE disk. */
635 if (strncmp ("ide/", p
, 4) == 0)
637 p
= strstr (p
, "part");
644 /* If this is a SCSI disk. */
645 if (strncmp ("scsi/", p
, 5) == 0)
647 p
= strstr (p
, "part");
654 /* If this is a DAC960 disk. */
655 if (strncmp ("rd/c", p
, 4) == 0)
657 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
665 /* If this is a CCISS disk. */
666 if (strncmp ("cciss/c", p
, sizeof ("cciss/c") - 1) == 0)
668 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
676 /* If this is a Compaq Intelligent Drive Array. */
677 if (strncmp ("ida/c", p
, sizeof ("ida/c") - 1) == 0)
679 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
687 /* If this is an I2O disk. */
688 if (strncmp ("i2o/hd", p
, sizeof ("i2o/hd") - 1) == 0)
690 /* /dev/i2o/hd[a-z]([0-9]+)? */
691 p
[sizeof ("i2o/hda") - 1] = '\0';
695 /* If this is a MultiMediaCard (MMC). */
696 if (strncmp ("mmcblk", p
, sizeof ("mmcblk") - 1) == 0)
698 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
706 /* If this is an IDE, SCSI or Virtio disk. */
707 if ((strncmp ("hd", p
, 2) == 0
708 || strncmp ("vd", p
, 2) == 0
709 || strncmp ("sd", p
, 2) == 0)
710 && p
[2] >= 'a' && p
[2] <= 'z')
712 /* /dev/[hsv]d[a-z][0-9]* */
717 /* If this is a Xen virtual block device. */
718 if ((strncmp ("xvd", p
, 3) == 0) && p
[3] >= 'a' && p
[3] <= 'z')
720 /* /dev/xvd[a-z][0-9]* */
728 #elif defined(__GNU__)
729 char *path
= xstrdup (os_dev
);
730 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
732 char *p
= strchr (path
+ 7, 's');
738 #elif defined(__CYGWIN__)
739 char *path
= xstrdup (os_dev
);
740 if (strncmp ("/dev/sd", path
, 7) == 0 && 'a' <= path
[7] && path
[7] <= 'z')
745 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
746 return xstrdup (os_dev
);
751 find_system_device (const char *os_dev
)
756 os_disk
= convert_system_partition_to_system_disk (os_dev
);
760 for (i
= 0; i
< (int) (sizeof (map
) / sizeof (map
[0])); i
++)
761 if (map
[i
].device
&& strcmp (map
[i
].device
, os_disk
) == 0)
772 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
777 if (stat (os_dev
, &st
) < 0)
779 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
783 drive
= find_system_device (os_dev
);
786 grub_error (GRUB_ERR_BAD_DEVICE
,
787 "no mapping exists for `%s'", os_dev
);
791 if (! S_ISBLK (st
.st_mode
))
792 return make_device_name (drive
, -1, -1);
794 #if defined(__linux__) || defined(__CYGWIN__)
795 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
796 partition, so mapping them to GRUB devices is not trivial.
797 Here, get the start sector of a partition by HDIO_GETGEO, and
798 compare it with each partition GRUB recognizes.
800 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
801 does not count the extended partition and missing primary
802 partitions. Use same method as on Linux here. */
807 struct hd_geometry hdg
;
810 auto int find_partition (grub_disk_t disk
,
811 const grub_partition_t partition
);
813 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
814 const grub_partition_t partition
)
816 struct grub_pc_partition
*pcdata
= NULL
;
818 if (strcmp (partition
->partmap
->name
, "pc_partition_map") == 0)
819 pcdata
= partition
->data
;
823 if (pcdata
->bsd_part
< 0)
824 grub_util_info ("DOS partition %d starts from %lu",
825 pcdata
->dos_part
, partition
->start
);
827 grub_util_info ("BSD partition %d,%c starts from %lu",
828 pcdata
->dos_part
, pcdata
->bsd_part
+ 'a',
833 grub_util_info ("Partition %d starts from %lu",
834 partition
->index
, partition
->start
);
837 if (hdg
.start
== partition
->start
)
841 dos_part
= pcdata
->dos_part
;
842 bsd_part
= pcdata
->bsd_part
;
846 dos_part
= partition
->index
;
855 name
= make_device_name (drive
, -1, -1);
857 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
860 fd
= open (os_dev
, O_RDONLY
);
863 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk geometry", os_dev
);
868 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
870 grub_error (GRUB_ERR_BAD_DEVICE
,
871 "cannot get geometry of `%s'", os_dev
);
879 grub_util_info ("%s starts from %lu", os_dev
, hdg
.start
);
884 grub_util_info ("opening the device %s", name
);
885 disk
= grub_disk_open (name
);
891 grub_partition_iterate (disk
, find_partition
);
892 if (grub_errno
!= GRUB_ERR_NONE
)
894 grub_disk_close (disk
);
900 grub_disk_close (disk
);
901 grub_error (GRUB_ERR_BAD_DEVICE
,
902 "cannot find the partition of `%s'", os_dev
);
906 return make_device_name (drive
, dos_part
, bsd_part
);
909 #elif defined(__GNU__)
910 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
916 p
= strrchr (os_dev
, 's');
923 n
= strtol (p
, &q
, 10);
924 if (p
!= q
&& n
!= LONG_MIN
&& n
!= LONG_MAX
)
928 if (*q
>= 'a' && *q
<= 'g')
933 return make_device_name (drive
, dos_part
, bsd_part
);
937 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
938 return make_device_name (drive
, -1, -1);