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>
33 #include <sys/types.h>
40 # include <sys/ioctl.h> /* ioctl */
41 # if !defined(__GLIBC__) || \
42 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
43 /* Maybe libc doesn't have large file support. */
44 # include <linux/unistd.h> /* _llseek */
45 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
47 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
48 # endif /* ! BLKFLSBUF */
49 # include <sys/ioctl.h> /* ioctl */
51 # define HDIO_GETGEO 0x0301 /* get device geometry */
52 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
57 unsigned char sectors
;
58 unsigned short cylinders
;
61 # endif /* ! HDIO_GETGEO */
63 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
64 # endif /* ! BLKGETSIZE64 */
68 # endif /* ! MINORBITS */
69 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
72 # define FLOPPY_MAJOR 2
73 # endif /* ! FLOPPY_MAJOR */
76 # endif /* ! LOOP_MAJOR */
77 #endif /* __linux__ */
79 static char *map
[256];
82 /* Check if we have devfs support. */
86 static int dev_devfsd_exists
= -1;
88 if (dev_devfsd_exists
< 0)
92 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
95 return dev_devfsd_exists
;
97 #endif /* __linux__ */
100 get_drive (const char *name
)
105 if ((name
[0] != 'f' && name
[0] != 'h') || name
[1] != 'd')
108 drive
= strtoul (name
+ 2, &p
, 10);
115 if (drive
> sizeof (map
) / sizeof (map
[0]))
121 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a biosdisk");
126 call_hook (int (*hook
) (const char *name
), int drive
)
130 sprintf (name
, (drive
& 0x80) ? "hd%d" : "fd%d", drive
& (~0x80));
135 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
139 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
140 if (map
[i
] && call_hook (hook
, i
))
147 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
152 drive
= get_drive (name
);
157 return grub_error (GRUB_ERR_BAD_DEVICE
,
158 "no mapping exists for `%s'", name
);
160 disk
->has_partitions
= (drive
& 0x80);
166 unsigned long long nr
;
169 fd
= open (map
[drive
], O_RDONLY
);
171 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", map
[drive
]);
173 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
179 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
186 disk
->total_sectors
= nr
/ 512;
189 grub_util_error ("unaligned device size");
191 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
193 return GRUB_ERR_NONE
;
197 /* In GNU/Hurd, stat() will return the right size. */
198 #elif !defined (__GNU__)
199 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
201 if (stat (map
[drive
], &st
) < 0)
202 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", map
[drive
]);
204 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
206 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
208 return GRUB_ERR_NONE
;
213 linux_find_partition (char *dev
, unsigned long sector
)
215 size_t len
= strlen (dev
);
219 char real_dev
[PATH_MAX
];
221 strcpy(real_dev
, dev
);
223 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
225 p
= real_dev
+ len
- 4;
228 else if ((strncmp (real_dev
+ 5, "hd", 2) == 0
229 || strncmp (real_dev
+ 5, "sd", 2) == 0)
230 && real_dev
[7] >= 'a' && real_dev
[7] <= 'z')
235 else if (strncmp (real_dev
+ 5, "rd/c", 4) == 0)
237 p
= strchr (real_dev
+ 9, 'd');
242 while (*p
&& isdigit (*p
))
250 for (i
= 1; i
< 10000; i
++)
253 struct hd_geometry hdg
;
255 sprintf (p
, format
, i
);
256 fd
= open (real_dev
, O_RDONLY
);
260 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
268 if (hdg
.start
== sector
)
270 strcpy (dev
, real_dev
);
277 #endif /* __linux__ */
280 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
285 flags
|= O_LARGEFILE
;
295 /* Linux has a bug that the disk cache for a whole disk is not consistent
296 with the one for a partition of the disk. */
298 int is_partition
= 0;
301 strcpy (dev
, map
[disk
->id
]);
302 if (disk
->partition
&& strncmp (map
[disk
->id
], "/dev/", 5) == 0)
303 is_partition
= linux_find_partition (dev
, disk
->partition
->start
);
305 /* Open the partition. */
306 grub_util_info ("opening the device `%s'", dev
);
307 fd
= open (dev
, flags
);
310 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
314 /* Make the buffer cache consistent with the physical disk. */
315 ioctl (fd
, BLKFLSBUF
, 0);
318 sector
-= disk
->partition
->start
;
320 #else /* ! __linux__ */
321 fd
= open (map
[disk
->id
], flags
);
324 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", map
[disk
->id
]);
327 #endif /* ! __linux__ */
329 #if defined(__linux__) && (!defined(__GLIBC__) || \
330 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
331 /* Maybe libc doesn't have large file support. */
333 loff_t offset
, result
;
334 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
335 loff_t
*res
, uint wh
);
336 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
337 loff_t
*, res
, uint
, wh
);
339 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
340 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
342 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
]);
349 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
351 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
353 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
]);
363 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
364 error occurs, otherwise return LEN. */
366 nread (int fd
, char *buf
, size_t len
)
372 ssize_t ret
= read (fd
, buf
, len
);
389 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
390 error occurs, otherwise return LEN. */
392 nwrite (int fd
, const char *buf
, size_t len
)
398 ssize_t ret
= write (fd
, buf
, len
);
416 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
417 grub_size_t size
, char *buf
)
421 fd
= open_device (disk
, sector
, O_RDONLY
);
426 if (sector
== 0 && size
> 1)
428 /* Work around a bug in Linux ez remapping. Linux remaps all
429 sectors that are read together with the MBR in one read. It
430 should only remap the MBR, so we split the read in two
432 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
434 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
]);
439 buf
+= GRUB_DISK_SECTOR_SIZE
;
442 #endif /* __linux__ */
444 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
445 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
446 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
]);
453 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
454 grub_size_t size
, const char *buf
)
458 fd
= open_device (disk
, sector
, O_WRONLY
);
462 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
463 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
464 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
]);
470 static struct grub_disk_dev grub_util_biosdisk_dev
=
473 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
474 .iterate
= grub_util_biosdisk_iterate
,
475 .open
= grub_util_biosdisk_open
,
477 .read
= grub_util_biosdisk_read
,
478 .write
= grub_util_biosdisk_write
,
483 read_device_map (const char *dev_map
)
486 char buf
[1024]; /* XXX */
488 auto void show_error (const char *msg
);
490 void show_error (const char *msg
)
492 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
495 fp
= fopen (dev_map
, "r");
497 grub_util_error ("Cannot open `%s'", dev_map
);
499 while (fgets (buf
, sizeof (buf
), fp
))
507 /* Skip leading spaces. */
508 while (*p
&& isspace (*p
))
511 /* If the first character is `#' or NUL, skip this line. */
512 if (*p
== '\0' || *p
== '#')
516 show_error ("No open parenthesis found");
519 drive
= get_drive (p
);
520 if (drive
< 0 || drive
>= (int) (sizeof (map
) / sizeof (map
[0])))
521 show_error ("Bad device name");
525 show_error ("No close parenthesis found");
528 /* Skip leading spaces. */
529 while (*p
&& isspace (*p
))
533 show_error ("No filename found");
535 /* NUL-terminate the filename. */
537 while (*e
&& ! isspace (*e
))
541 /* Multiple entries for a given drive is not allowed. */
543 show_error ("Duplicated entry found");
546 /* On Linux, the devfs uses symbolic links horribly, and that
547 confuses the interface very much, so use realpath to expand
549 map
[drive
] = xmalloc (PATH_MAX
);
550 if (! realpath (p
, map
[drive
]))
551 grub_util_error ("Cannot get the real path of `%s'", p
);
553 map
[drive
] = xstrdup (p
);
561 grub_util_biosdisk_init (const char *dev_map
)
563 read_device_map (dev_map
);
564 grub_disk_dev_register (&grub_util_biosdisk_dev
);
568 grub_util_biosdisk_fini (void)
572 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
575 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
579 make_device_name (int drive
, int dos_part
, int bsd_part
)
584 sprintf (p
, (drive
& 0x80) ? "hd%d" : "fd%d", drive
& ~0x80);
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
)
600 #if defined(__linux__)
601 path
= xmalloc (PATH_MAX
);
602 if (! realpath (os_dev
, path
))
605 if (strncmp ("/dev/", path
, 5) == 0)
609 /* If this is an IDE disk. */
610 if (strncmp ("ide/", p
, 4) == 0)
612 p
= strstr (p
, "part");
619 /* If this is a SCSI disk. */
620 if (strncmp ("scsi/", p
, 5) == 0)
622 p
= strstr (p
, "part");
629 /* If this is a DAC960 disk. */
630 if (strncmp ("rd/c", p
, 4) == 0)
632 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
640 /* If this is an IDE disk or a SCSI disk. */
641 if ((strncmp ("hd", p
, 2) == 0
642 || strncmp ("sd", p
, 2) == 0)
643 && p
[2] >= 'a' && p
[2] <= 'z')
645 /* /dev/[hs]d[a-z][0-9]* */
653 #elif defined(__GNU__)
654 path
= xstrdup (os_dev
);
655 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
657 p
= strchr (path
, 's');
664 # warning "The function `get_os_disk' might not work on your OS correctly."
665 return xstrdup (os_dev
);
670 find_drive (const char *os_dev
)
675 os_disk
= get_os_disk (os_dev
);
679 for (i
= 0; i
< (int) (sizeof (map
) / sizeof (map
[0])); i
++)
680 if (map
[i
] && strcmp (map
[i
], os_disk
) == 0)
691 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
696 if (stat (os_dev
, &st
) < 0)
698 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
702 drive
= find_drive (os_dev
);
705 grub_error (GRUB_ERR_BAD_DEVICE
,
706 "no mapping exists for `%s'", os_dev
);
710 if (! S_ISBLK (st
.st_mode
))
711 return make_device_name (drive
, -1, -1);
713 #if defined(__linux__)
714 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
715 partition, so mapping them to GRUB devices is not trivial.
716 Here, get the start sector of a partition by HDIO_GETGEO, and
717 compare it with each partition GRUB recognizes. */
722 struct hd_geometry hdg
;
725 auto int find_partition (grub_disk_t disk
,
726 const grub_partition_t partition
);
728 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
729 const grub_partition_t partition
)
731 struct grub_pc_partition
*pcdata
= NULL
;
733 if (strcmp (partition
->partmap
->name
, "pc_partition_map") == 0)
734 pcdata
= partition
->data
;
738 if (pcdata
->bsd_part
< 0)
739 grub_util_info ("DOS partition %d starts from %lu",
740 pcdata
->dos_part
, partition
->start
);
742 grub_util_info ("BSD partition %d,%c starts from %lu",
743 pcdata
->dos_part
, pcdata
->bsd_part
+ 'a',
748 grub_util_info ("Partition %d starts from %lu",
749 partition
->index
, partition
->start
);
752 if (hdg
.start
== partition
->start
)
756 dos_part
= pcdata
->dos_part
;
757 bsd_part
= pcdata
->bsd_part
;
761 dos_part
= partition
->index
;
770 name
= make_device_name (drive
, -1, -1);
772 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
775 fd
= open (os_dev
, O_RDONLY
);
778 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", os_dev
);
783 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
785 grub_error (GRUB_ERR_BAD_DEVICE
,
786 "cannot get geometry of `%s'", os_dev
);
794 grub_util_info ("%s starts from %lu", os_dev
, hdg
.start
);
799 grub_util_info ("opening the device %s", name
);
800 disk
= grub_disk_open (name
);
806 if (grub_partition_iterate (disk
, find_partition
) != GRUB_ERR_NONE
)
808 grub_disk_close (disk
);
814 grub_disk_close (disk
);
815 grub_error (GRUB_ERR_BAD_DEVICE
,
816 "cannot find the partition of `%s'", os_dev
);
820 return make_device_name (drive
, dos_part
, bsd_part
);
823 #elif defined(__GNU__)
824 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
830 p
= strrchr (os_dev
, 's');
837 n
= strtol (p
, &q
, 10);
838 if (p
!= q
&& n
!= LONG_MIN
&& n
!= LONG_MAX
)
842 if (*q
>= 'a' && *q
<= 'g')
847 return make_device_name (drive
, dos_part
, bsd_part
);
851 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
852 return make_device_name (drive
, -1, -1);