2009-04-11 Felix Zielcke <fzielcke@z-51.de>
[grub2/phcoder/solaris.git] / util / hostdisk.c
blob2ef2057860e8b14ebd1b9b5c429d85b37ca55e62
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 #endif
93 struct
95 char *drive;
96 char *device;
97 } map[256];
99 #ifdef __linux__
100 /* Check if we have devfs support. */
101 static int
102 have_devfs (void)
104 static int dev_devfsd_exists = -1;
106 if (dev_devfsd_exists < 0)
108 struct stat st;
110 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
113 return dev_devfsd_exists;
115 #endif /* __linux__ */
117 static int
118 find_grub_drive (const char *name)
120 unsigned int i;
122 if (name)
124 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
125 if (map[i].drive && ! strcmp (map[i].drive, name))
126 return i;
129 return -1;
132 static int
133 find_free_slot ()
135 unsigned int i;
137 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
138 if (! map[i].drive)
139 return i;
141 return -1;
144 static int
145 grub_util_biosdisk_iterate (int (*hook) (const char *name))
147 unsigned i;
149 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
150 if (map[i].drive && hook (map[i].drive))
151 return 1;
153 return 0;
156 static grub_err_t
157 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
159 int drive;
160 struct stat st;
162 drive = find_grub_drive (name);
163 if (drive < 0)
164 return grub_error (GRUB_ERR_BAD_DEVICE,
165 "no mapping exists for `%s'", name);
167 disk->has_partitions = 1;
168 disk->id = drive;
170 /* Get the size. */
171 #if defined(__MINGW32__)
173 grub_uint64_t size;
175 size = grub_util_get_disk_size (map[drive].device);
177 if (size % 512)
178 grub_util_error ("unaligned device size");
180 disk->total_sectors = size >> 9;
182 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
184 return GRUB_ERR_NONE;
186 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
188 unsigned long long nr;
189 int fd;
191 fd = open (map[drive].device, O_RDONLY);
192 if (fd == -1)
193 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
195 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
196 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
197 # else
198 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
199 # endif
201 close (fd);
202 goto fail;
205 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
206 if (ioctl (fd, DIOCGMEDIASIZE, &nr))
207 # else
208 if (ioctl (fd, BLKGETSIZE64, &nr))
209 # endif
211 close (fd);
212 goto fail;
215 close (fd);
216 disk->total_sectors = nr / 512;
218 if (nr % 512)
219 grub_util_error ("unaligned device size");
221 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
223 return GRUB_ERR_NONE;
226 fail:
227 /* In GNU/Hurd, stat() will return the right size. */
228 #elif !defined (__GNU__)
229 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
230 #endif
231 if (stat (map[drive].device, &st) < 0)
232 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
234 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
236 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
238 return GRUB_ERR_NONE;
241 #ifdef __linux__
242 static int
243 linux_find_partition (char *dev, unsigned long sector)
245 size_t len = strlen (dev);
246 const char *format;
247 char *p;
248 int i;
249 char real_dev[PATH_MAX];
251 strcpy(real_dev, dev);
253 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
255 p = real_dev + len - 4;
256 format = "part%d";
258 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
260 p = real_dev + len;
261 format = "p%d";
263 else
265 p = real_dev + len;
266 format = "%d";
269 for (i = 1; i < 10000; i++)
271 int fd;
272 struct hd_geometry hdg;
274 sprintf (p, format, i);
275 fd = open (real_dev, O_RDONLY);
276 if (fd == -1)
277 return 0;
279 if (ioctl (fd, HDIO_GETGEO, &hdg))
281 close (fd);
282 return 0;
285 close (fd);
287 if (hdg.start == sector)
289 strcpy (dev, real_dev);
290 return 1;
294 return 0;
296 #endif /* __linux__ */
298 static int
299 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
301 int fd;
303 #ifdef O_LARGEFILE
304 flags |= O_LARGEFILE;
305 #endif
306 #ifdef O_SYNC
307 flags |= O_SYNC;
308 #endif
309 #ifdef O_FSYNC
310 flags |= O_FSYNC;
311 #endif
312 #ifdef O_BINARY
313 flags |= O_BINARY;
314 #endif
316 #ifdef __linux__
317 /* Linux has a bug that the disk cache for a whole disk is not consistent
318 with the one for a partition of the disk. */
320 int is_partition = 0;
321 char dev[PATH_MAX];
323 strcpy (dev, map[disk->id].device);
324 if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0)
325 is_partition = linux_find_partition (dev, disk->partition->start);
327 /* Open the partition. */
328 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev);
329 fd = open (dev, flags);
330 if (fd < 0)
332 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
333 return -1;
336 /* Make the buffer cache consistent with the physical disk. */
337 ioctl (fd, BLKFLSBUF, 0);
339 if (is_partition)
340 sector -= disk->partition->start;
342 #else /* ! __linux__ */
343 fd = open (map[disk->id].device, flags);
344 if (fd < 0)
346 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
347 return -1;
349 #endif /* ! __linux__ */
351 #if defined(__linux__) && (!defined(__GLIBC__) || \
352 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
353 /* Maybe libc doesn't have large file support. */
355 loff_t offset, result;
356 static int _llseek (uint filedes, ulong hi, ulong lo,
357 loff_t *res, uint wh);
358 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
359 loff_t *, res, uint, wh);
361 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
362 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
364 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
365 close (fd);
366 return -1;
369 #else
371 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
373 if (lseek (fd, offset, SEEK_SET) != offset)
375 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
376 close (fd);
377 return -1;
380 #endif
382 return fd;
385 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
386 error occurs, otherwise return LEN. */
387 static ssize_t
388 nread (int fd, char *buf, size_t len)
390 ssize_t size = len;
392 while (len)
394 ssize_t ret = read (fd, buf, len);
396 if (ret <= 0)
398 if (errno == EINTR)
399 continue;
400 else
401 return ret;
404 len -= ret;
405 buf += ret;
408 return size;
411 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
412 error occurs, otherwise return LEN. */
413 static ssize_t
414 nwrite (int fd, const char *buf, size_t len)
416 ssize_t size = len;
418 while (len)
420 ssize_t ret = write (fd, buf, len);
422 if (ret <= 0)
424 if (errno == EINTR)
425 continue;
426 else
427 return ret;
430 len -= ret;
431 buf += ret;
434 return size;
437 static grub_err_t
438 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
439 grub_size_t size, char *buf)
441 int fd;
443 fd = open_device (disk, sector, O_RDONLY);
444 if (fd < 0)
445 return grub_errno;
447 #ifdef __linux__
448 if (sector == 0 && size > 1)
450 /* Work around a bug in Linux ez remapping. Linux remaps all
451 sectors that are read together with the MBR in one read. It
452 should only remap the MBR, so we split the read in two
453 parts. -jochen */
454 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
456 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
457 close (fd);
458 return grub_errno;
461 buf += GRUB_DISK_SECTOR_SIZE;
462 size--;
464 #endif /* __linux__ */
466 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
467 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
468 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
470 close (fd);
471 return grub_errno;
474 static grub_err_t
475 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
476 grub_size_t size, const char *buf)
478 int fd;
480 fd = open_device (disk, sector, O_WRONLY);
481 if (fd < 0)
482 return grub_errno;
484 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
485 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
486 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
488 close (fd);
489 return grub_errno;
492 static struct grub_disk_dev grub_util_biosdisk_dev =
494 .name = "biosdisk",
495 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
496 .iterate = grub_util_biosdisk_iterate,
497 .open = grub_util_biosdisk_open,
498 .close = 0,
499 .read = grub_util_biosdisk_read,
500 .write = grub_util_biosdisk_write,
501 .next = 0
504 static void
505 read_device_map (const char *dev_map)
507 FILE *fp;
508 char buf[1024]; /* XXX */
509 int lineno = 0;
510 struct stat st;
512 auto void show_error (const char *msg);
513 void show_error (const char *msg)
515 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
518 fp = fopen (dev_map, "r");
519 if (! fp)
520 grub_util_error ("Cannot open `%s'", dev_map);
522 while (fgets (buf, sizeof (buf), fp))
524 char *p = buf;
525 char *e;
526 int drive;
528 lineno++;
530 /* Skip leading spaces. */
531 while (*p && isspace (*p))
532 p++;
534 /* If the first character is `#' or NUL, skip this line. */
535 if (*p == '\0' || *p == '#')
536 continue;
538 if (*p != '(')
539 show_error ("No open parenthesis found");
541 p++;
542 /* Find a free slot. */
543 drive = find_free_slot ();
544 if (drive < 0)
545 show_error ("Map table size exceeded");
547 e = p;
548 p = strchr (p, ')');
549 if (! p)
550 show_error ("No close parenthesis found");
552 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
553 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
554 map[drive].drive[p - e] = '\0';
556 p++;
557 /* Skip leading spaces. */
558 while (*p && isspace (*p))
559 p++;
561 if (*p == '\0')
562 show_error ("No filename found");
564 /* NUL-terminate the filename. */
565 e = p;
566 while (*e && ! isspace (*e))
567 e++;
568 *e = '\0';
570 #ifdef __MINGW32__
571 (void) st;
572 if (grub_util_get_disk_size (p) == -1LL)
573 #else
574 if (stat (p, &st) == -1)
575 #endif
577 free (map[drive].drive);
578 map[drive].drive = NULL;
579 grub_util_info ("Cannot stat `%s', skipping", p);
580 continue;
583 #ifdef __linux__
584 /* On Linux, the devfs uses symbolic links horribly, and that
585 confuses the interface very much, so use realpath to expand
586 symbolic links. */
587 map[drive].device = xmalloc (PATH_MAX);
588 if (! realpath (p, map[drive].device))
589 grub_util_error ("Cannot get the real path of `%s'", p);
590 #else
591 map[drive].device = xstrdup (p);
592 #endif
595 fclose (fp);
598 void
599 grub_util_biosdisk_init (const char *dev_map)
601 read_device_map (dev_map);
602 grub_disk_dev_register (&grub_util_biosdisk_dev);
605 void
606 grub_util_biosdisk_fini (void)
608 unsigned i;
610 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
612 if (map[i].drive)
613 free (map[i].drive);
614 if (map[i].device)
615 free (map[i].device);
616 map[i].drive = map[i].device = NULL;
619 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
622 static char *
623 make_device_name (int drive, int dos_part, int bsd_part)
625 char *p;
627 p = xmalloc (30);
628 sprintf (p, "%s", map[drive].drive);
630 if (dos_part >= 0)
631 sprintf (p + strlen (p), ",%d", dos_part + 1);
633 if (bsd_part >= 0)
634 sprintf (p + strlen (p), ",%c", bsd_part + 'a');
636 return p;
639 static char *
640 convert_system_partition_to_system_disk (const char *os_dev)
642 #if defined(__linux__)
643 char *path = xmalloc (PATH_MAX);
644 if (! realpath (os_dev, path))
645 return 0;
647 if (strncmp ("/dev/", path, 5) == 0)
649 char *p = path + 5;
651 /* If this is an IDE disk. */
652 if (strncmp ("ide/", p, 4) == 0)
654 p = strstr (p, "part");
655 if (p)
656 strcpy (p, "disc");
658 return path;
661 /* If this is a SCSI disk. */
662 if (strncmp ("scsi/", p, 5) == 0)
664 p = strstr (p, "part");
665 if (p)
666 strcpy (p, "disc");
668 return path;
671 /* If this is a DAC960 disk. */
672 if (strncmp ("rd/c", p, 4) == 0)
674 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
675 p = strchr (p, 'p');
676 if (p)
677 *p = '\0';
679 return path;
682 /* If this is a CCISS disk. */
683 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
685 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
686 p = strchr (p, 'p');
687 if (p)
688 *p = '\0';
690 return path;
693 /* If this is a Compaq Intelligent Drive Array. */
694 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
696 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
697 p = strchr (p, 'p');
698 if (p)
699 *p = '\0';
701 return path;
704 /* If this is an I2O disk. */
705 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
707 /* /dev/i2o/hd[a-z]([0-9]+)? */
708 p[sizeof ("i2o/hda") - 1] = '\0';
709 return path;
712 /* If this is a MultiMediaCard (MMC). */
713 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
715 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
716 p = strchr (p, 'p');
717 if (p)
718 *p = '\0';
720 return path;
723 /* If this is an IDE, SCSI or Virtio disk. */
724 if ((strncmp ("hd", p, 2) == 0
725 || strncmp ("vd", p, 2) == 0
726 || strncmp ("sd", p, 2) == 0)
727 && p[2] >= 'a' && p[2] <= 'z')
729 /* /dev/[hsv]d[a-z][0-9]* */
730 p[3] = '\0';
731 return path;
734 /* If this is a Xen virtual block device. */
735 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
737 /* /dev/xvd[a-z][0-9]* */
738 p[4] = '\0';
739 return path;
743 return path;
745 #elif defined(__GNU__)
746 char *path = xstrdup (os_dev);
747 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
749 char *p = strchr (path + 7, 's');
750 if (p)
751 *p = '\0';
753 return path;
755 #elif defined(__CYGWIN__)
756 char *path = xstrdup (os_dev);
757 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
758 path[8] = 0;
759 return path;
761 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
762 char *path = xstrdup (os_dev);
763 if (strncmp ("/dev/", path, 5) == 0)
765 char *p;
766 for (p = path + 5; *p; ++p)
767 if (grub_isdigit(*p))
769 p = strchr (p, 's');
770 if (p)
771 *p = '\0';
772 break;
775 return path;
777 #else
778 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
779 return xstrdup (os_dev);
780 #endif
783 static int
784 find_system_device (const char *os_dev)
786 int i;
787 char *os_disk;
789 os_disk = convert_system_partition_to_system_disk (os_dev);
790 if (! os_disk)
791 return -1;
793 for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
794 if (map[i].device && strcmp (map[i].device, os_disk) == 0)
796 free (os_disk);
797 return i;
800 free (os_disk);
801 return -1;
804 char *
805 grub_util_biosdisk_get_grub_dev (const char *os_dev)
807 struct stat st;
808 int drive;
810 if (stat (os_dev, &st) < 0)
812 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
813 return 0;
816 drive = find_system_device (os_dev);
817 if (drive < 0)
819 grub_error (GRUB_ERR_BAD_DEVICE,
820 "no mapping exists for `%s'", os_dev);
821 return 0;
824 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
825 if (! S_ISCHR (st.st_mode))
826 #else
827 if (! S_ISBLK (st.st_mode))
828 #endif
829 return make_device_name (drive, -1, -1);
831 #if defined(__linux__) || defined(__CYGWIN__)
832 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
833 partition, so mapping them to GRUB devices is not trivial.
834 Here, get the start sector of a partition by HDIO_GETGEO, and
835 compare it with each partition GRUB recognizes.
837 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
838 does not count the extended partition and missing primary
839 partitions. Use same method as on Linux here. */
841 char *name;
842 grub_disk_t disk;
843 int fd;
844 struct hd_geometry hdg;
845 int dos_part = -1;
846 int bsd_part = -1;
847 auto int find_partition (grub_disk_t disk,
848 const grub_partition_t partition);
850 int find_partition (grub_disk_t disk __attribute__ ((unused)),
851 const grub_partition_t partition)
853 struct grub_pc_partition *pcdata = NULL;
855 if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
856 pcdata = partition->data;
858 if (pcdata)
860 if (pcdata->bsd_part < 0)
861 grub_util_info ("DOS partition %d starts from %lu",
862 pcdata->dos_part, partition->start);
863 else
864 grub_util_info ("BSD partition %d,%c starts from %lu",
865 pcdata->dos_part, pcdata->bsd_part + 'a',
866 partition->start);
868 else
870 grub_util_info ("Partition %d starts from %lu",
871 partition->index, partition->start);
874 if (hdg.start == partition->start)
876 if (pcdata)
878 dos_part = pcdata->dos_part;
879 bsd_part = pcdata->bsd_part;
881 else
883 dos_part = partition->index;
884 bsd_part = -1;
886 return 1;
889 return 0;
892 name = make_device_name (drive, -1, -1);
894 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
895 return name;
897 fd = open (os_dev, O_RDONLY);
898 if (fd == -1)
900 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk geometry", os_dev);
901 free (name);
902 return 0;
905 if (ioctl (fd, HDIO_GETGEO, &hdg))
907 grub_error (GRUB_ERR_BAD_DEVICE,
908 "cannot get geometry of `%s'", os_dev);
909 close (fd);
910 free (name);
911 return 0;
914 close (fd);
916 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
918 if (hdg.start == 0)
919 return name;
921 grub_util_info ("opening the device %s", name);
922 disk = grub_disk_open (name);
923 free (name);
925 if (! disk)
926 return 0;
928 grub_partition_iterate (disk, find_partition);
929 if (grub_errno != GRUB_ERR_NONE)
931 grub_disk_close (disk);
932 return 0;
935 if (dos_part < 0)
937 grub_disk_close (disk);
938 grub_error (GRUB_ERR_BAD_DEVICE,
939 "cannot find the partition of `%s'", os_dev);
940 return 0;
943 return make_device_name (drive, dos_part, bsd_part);
946 #elif defined(__GNU__)
947 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
949 char *p;
950 int dos_part = -1;
951 int bsd_part = -1;
953 p = strrchr (os_dev, 's');
954 if (p)
956 long int n;
957 char *q;
959 p++;
960 n = strtol (p, &q, 10);
961 if (p != q && n != LONG_MIN && n != LONG_MAX)
963 dos_part = (int) n;
965 if (*q >= 'a' && *q <= 'g')
966 bsd_part = *q - 'a';
970 return make_device_name (drive, dos_part, bsd_part);
973 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
974 /* FreeBSD uses "/dev/[a-z]+[0-9]+(s[0-9]+[a-z]?)?". */
976 int dos_part = -1;
977 int bsd_part = -1;
979 if (strncmp ("/dev/", os_dev, 5) == 0)
981 char *p, *q;
982 long int n;
984 for (p = os_dev + 5; *p; ++p)
985 if (grub_isdigit(*p))
987 p = strchr (p, 's');
988 if (p)
990 p++;
991 n = strtol (p, &q, 10);
992 if (p != q && n != LONG_MIN && n != LONG_MAX)
994 dos_part = (int) n - 1;
996 if (*q >= 'a' && *q <= 'g')
997 bsd_part = *q - 'a';
1000 break;
1004 return make_device_name (drive, dos_part, bsd_part);
1007 #else
1008 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1009 return make_device_name (drive, -1, -1);
1010 #endif