2008-08-28 Robert Millan <rmh@aybabtu.com>
[grub2/jjazz.git] / util / biosdisk.c
blob9fc1583734f976689c13ba0046402810fdbebd87
1 /* biosdisk.c - emulate biosdisk */
2 /*
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>
24 #include <grub/err.h>
25 #include <grub/util/misc.h>
26 #include <grub/util/biosdisk.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <assert.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <limits.h>
40 #ifdef __linux__
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)) */
47 # ifndef BLKFLSBUF
48 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
49 # endif /* ! BLKFLSBUF */
50 # include <sys/ioctl.h> /* ioctl */
51 # ifndef HDIO_GETGEO
52 # define HDIO_GETGEO 0x0301 /* get device geometry */
53 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
54 defined. */
55 struct hd_geometry
57 unsigned char heads;
58 unsigned char sectors;
59 unsigned short cylinders;
60 unsigned long start;
62 # endif /* ! HDIO_GETGEO */
63 # ifndef BLKGETSIZE64
64 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
65 # endif /* ! BLKGETSIZE64 */
66 # ifndef MAJOR
67 # ifndef MINORBITS
68 # define MINORBITS 8
69 # endif /* ! MINORBITS */
70 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
71 # endif /* ! MAJOR */
72 # ifndef FLOPPY_MAJOR
73 # define FLOPPY_MAJOR 2
74 # endif /* ! FLOPPY_MAJOR */
75 # ifndef LOOP_MAJOR
76 # define LOOP_MAJOR 7
77 # endif /* ! LOOP_MAJOR */
78 #endif /* __linux__ */
80 #ifdef __CYGWIN__
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
86 #endif
88 struct
90 char *drive;
91 char *device;
92 } map[256];
94 #ifdef __linux__
95 /* Check if we have devfs support. */
96 static int
97 have_devfs (void)
99 static int dev_devfsd_exists = -1;
101 if (dev_devfsd_exists < 0)
103 struct stat st;
105 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
108 return dev_devfsd_exists;
110 #endif /* __linux__ */
112 static int
113 find_grub_drive (const char *name)
115 unsigned int i;
117 if (name)
119 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
120 if (map[i].drive && ! strcmp (map[i].drive, name))
121 return i;
124 return -1;
127 static int
128 find_free_slot ()
130 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
131 if (! map[i].drive)
132 return i;
134 return -1;
137 static int
138 grub_util_biosdisk_iterate (int (*hook) (const char *name))
140 unsigned i;
142 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
143 if (map[i].drive && hook (map[i].drive))
144 return 1;
146 return 0;
149 static grub_err_t
150 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
152 int drive;
153 struct stat st;
155 drive = find_grub_drive (name);
156 if (drive < 0)
157 return grub_error (GRUB_ERR_BAD_DEVICE,
158 "no mapping exists for `%s'", name);
160 disk->has_partitions = 1;
161 disk->id = drive;
163 /* Get the size. */
164 #if defined(__linux__) || defined(__CYGWIN__)
166 unsigned long long nr;
167 int fd;
169 fd = open (map[drive].device, O_RDONLY);
170 if (fd == -1)
171 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
173 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
175 close (fd);
176 goto fail;
179 if (ioctl (fd, BLKGETSIZE64, &nr))
181 close (fd);
182 goto fail;
185 close (fd);
186 disk->total_sectors = nr / 512;
188 if (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;
196 fail:
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."
200 #endif
201 if (stat (map[drive].device, &st) < 0)
202 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
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;
211 #ifdef __linux__
212 static int
213 linux_find_partition (char *dev, unsigned long sector)
215 size_t len = strlen (dev);
216 const char *format;
217 char *p;
218 int i;
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;
226 format = "part%d";
228 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
230 p = real_dev + len;
231 format = "p%d";
233 else
235 p = real_dev + len;
236 format = "%d";
239 for (i = 1; i < 10000; i++)
241 int fd;
242 struct hd_geometry hdg;
244 sprintf (p, format, i);
245 fd = open (real_dev, O_RDONLY);
246 if (fd == -1)
247 return 0;
249 if (ioctl (fd, HDIO_GETGEO, &hdg))
251 close (fd);
252 return 0;
255 close (fd);
257 if (hdg.start == sector)
259 strcpy (dev, real_dev);
260 return 1;
264 return 0;
266 #endif /* __linux__ */
268 static int
269 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
271 int fd;
273 #ifdef O_LARGEFILE
274 flags |= O_LARGEFILE;
275 #endif
276 #ifdef O_SYNC
277 flags |= O_SYNC;
278 #endif
279 #ifdef O_FSYNC
280 flags |= O_FSYNC;
281 #endif
283 #ifdef __linux__
284 /* Linux has a bug that the disk cache for a whole disk is not consistent
285 with the one for a partition of the disk. */
287 int is_partition = 0;
288 char dev[PATH_MAX];
290 strcpy (dev, map[disk->id].device);
291 if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0)
292 is_partition = linux_find_partition (dev, disk->partition->start);
294 /* Open the partition. */
295 grub_util_info ("opening the device `%s' in open_device()", dev);
296 fd = open (dev, flags);
297 if (fd < 0)
299 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
300 return -1;
303 /* Make the buffer cache consistent with the physical disk. */
304 ioctl (fd, BLKFLSBUF, 0);
306 if (is_partition)
307 sector -= disk->partition->start;
309 #else /* ! __linux__ */
310 fd = open (map[disk->id].device, flags);
311 if (fd < 0)
313 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
314 return -1;
316 #endif /* ! __linux__ */
318 #if defined(__linux__) && (!defined(__GLIBC__) || \
319 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
320 /* Maybe libc doesn't have large file support. */
322 loff_t offset, result;
323 static int _llseek (uint filedes, ulong hi, ulong lo,
324 loff_t *res, uint wh);
325 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
326 loff_t *, res, uint, wh);
328 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
329 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
331 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
332 close (fd);
333 return -1;
336 #else
338 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
340 if (lseek (fd, offset, SEEK_SET) != offset)
342 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
343 close (fd);
344 return -1;
347 #endif
349 return fd;
352 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
353 error occurs, otherwise return LEN. */
354 static ssize_t
355 nread (int fd, char *buf, size_t len)
357 ssize_t size = len;
359 while (len)
361 ssize_t ret = read (fd, buf, len);
363 if (ret <= 0)
365 if (errno == EINTR)
366 continue;
367 else
368 return ret;
371 len -= ret;
372 buf += ret;
375 return size;
378 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
379 error occurs, otherwise return LEN. */
380 static ssize_t
381 nwrite (int fd, const char *buf, size_t len)
383 ssize_t size = len;
385 while (len)
387 ssize_t ret = write (fd, buf, len);
389 if (ret <= 0)
391 if (errno == EINTR)
392 continue;
393 else
394 return ret;
397 len -= ret;
398 buf += ret;
401 return size;
404 static grub_err_t
405 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
406 grub_size_t size, char *buf)
408 int fd;
410 fd = open_device (disk, sector, O_RDONLY);
411 if (fd < 0)
412 return grub_errno;
414 #ifdef __linux__
415 if (sector == 0 && size > 1)
417 /* Work around a bug in Linux ez remapping. Linux remaps all
418 sectors that are read together with the MBR in one read. It
419 should only remap the MBR, so we split the read in two
420 parts. -jochen */
421 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
423 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
424 close (fd);
425 return grub_errno;
428 buf += GRUB_DISK_SECTOR_SIZE;
429 size--;
431 #endif /* __linux__ */
433 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
434 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
435 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
437 close (fd);
438 return grub_errno;
441 static grub_err_t
442 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
443 grub_size_t size, const char *buf)
445 int fd;
447 fd = open_device (disk, sector, O_WRONLY);
448 if (fd < 0)
449 return grub_errno;
451 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
452 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
453 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
455 close (fd);
456 return grub_errno;
459 static struct grub_disk_dev grub_util_biosdisk_dev =
461 .name = "biosdisk",
462 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
463 .iterate = grub_util_biosdisk_iterate,
464 .open = grub_util_biosdisk_open,
465 .close = 0,
466 .read = grub_util_biosdisk_read,
467 .write = grub_util_biosdisk_write,
468 .next = 0
471 static void
472 read_device_map (const char *dev_map)
474 FILE *fp;
475 char buf[1024]; /* XXX */
476 int lineno = 0;
477 struct stat st;
479 auto void show_error (const char *msg);
480 void show_error (const char *msg)
482 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
485 fp = fopen (dev_map, "r");
486 if (! fp)
487 grub_util_error ("Cannot open `%s'", dev_map);
489 while (fgets (buf, sizeof (buf), fp))
491 char *p = buf;
492 char *e;
493 int drive;
495 lineno++;
497 /* Skip leading spaces. */
498 while (*p && isspace (*p))
499 p++;
501 /* If the first character is `#' or NUL, skip this line. */
502 if (*p == '\0' || *p == '#')
503 continue;
505 if (*p != '(')
506 show_error ("No open parenthesis found");
508 p++;
509 /* Find a free slot. */
510 drive = find_free_slot ();
511 if (drive < 0)
512 show_error ("Map table size exceeded");
514 e = p;
515 p = strchr (p, ')');
516 if (! p)
517 show_error ("No close parenthesis found");
519 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
520 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
521 map[drive].drive[p - e] = '\0';
523 p++;
524 /* Skip leading spaces. */
525 while (*p && isspace (*p))
526 p++;
528 if (*p == '\0')
529 show_error ("No filename found");
531 /* NUL-terminate the filename. */
532 e = p;
533 while (*e && ! isspace (*e))
534 e++;
535 *e = '\0';
537 if (stat (p, &st) == -1)
539 free (map[drive].drive);
540 map[drive].drive = NULL;
541 grub_util_info ("Cannot stat `%s', skipping", p);
542 continue;
545 #ifdef __linux__
546 /* On Linux, the devfs uses symbolic links horribly, and that
547 confuses the interface very much, so use realpath to expand
548 symbolic links. */
549 map[drive].device = xmalloc (PATH_MAX);
550 if (! realpath (p, map[drive].device))
551 grub_util_error ("Cannot get the real path of `%s'", p);
552 #else
553 map[drive].device = xstrdup (p);
554 #endif
557 fclose (fp);
560 void
561 grub_util_biosdisk_init (const char *dev_map)
563 read_device_map (dev_map);
564 grub_disk_dev_register (&grub_util_biosdisk_dev);
567 void
568 grub_util_biosdisk_fini (void)
570 unsigned i;
572 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
574 if (map[i].drive)
575 free (map[i].drive);
576 if (map[i].device)
577 free (map[i].device);
578 map[i].drive = map[i].device = NULL;
581 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
584 static char *
585 make_device_name (int drive, int dos_part, int bsd_part)
587 char *p;
589 p = xmalloc (30);
590 sprintf (p, "%s", map[drive].drive);
592 if (dos_part >= 0)
593 sprintf (p + strlen (p), ",%d", dos_part + 1);
595 if (bsd_part >= 0)
596 sprintf (p + strlen (p), ",%c", bsd_part + 'a');
598 return p;
601 static char *
602 convert_system_partition_to_system_disk (const char *os_dev)
604 #if defined(__linux__)
605 char *path = xmalloc (PATH_MAX);
606 if (! realpath (os_dev, path))
607 return 0;
609 if (strncmp ("/dev/", path, 5) == 0)
611 char *p = path + 5;
613 /* If this is an IDE disk. */
614 if (strncmp ("ide/", p, 4) == 0)
616 p = strstr (p, "part");
617 if (p)
618 strcpy (p, "disc");
620 return path;
623 /* If this is a SCSI disk. */
624 if (strncmp ("scsi/", p, 5) == 0)
626 p = strstr (p, "part");
627 if (p)
628 strcpy (p, "disc");
630 return path;
633 /* If this is a DAC960 disk. */
634 if (strncmp ("rd/c", p, 4) == 0)
636 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
637 p = strchr (p, 'p');
638 if (p)
639 *p = '\0';
641 return path;
644 /* If this is a CCISS disk. */
645 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
647 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
648 p = strchr (p, 'p');
649 if (p)
650 *p = '\0';
652 return path;
655 /* If this is a Compaq Intelligent Drive Array. */
656 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
658 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
659 p = strchr (p, 'p');
660 if (p)
661 *p = '\0';
663 return path;
666 /* If this is an I2O disk. */
667 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
669 /* /dev/i2o/hd[a-z]([0-9]+)? */
670 p[sizeof ("i2o/hda") - 1] = '\0';
671 return path;
674 /* If this is a MultiMediaCard (MMC). */
675 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
677 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
678 p = strchr (p, 'p');
679 if (p)
680 *p = '\0';
682 return path;
685 /* If this is an IDE, SCSI or Virtio disk. */
686 if ((strncmp ("hd", p, 2) == 0
687 || strncmp ("vd", p, 2) == 0
688 || strncmp ("sd", p, 2) == 0)
689 && p[2] >= 'a' && p[2] <= 'z')
691 /* /dev/[hsv]d[a-z][0-9]* */
692 p[3] = '\0';
693 return path;
696 /* If this is a Xen virtual block device. */
697 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
699 /* /dev/xvd[a-z][0-9]* */
700 p[4] = '\0';
701 return path;
705 return path;
707 #elif defined(__GNU__)
708 char *path = xstrdup (os_dev);
709 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
711 char *p = strchr (path + 7, 's');
712 if (p)
713 *p = '\0';
715 return path;
717 #elif defined(__CYGWIN__)
718 char *path = xstrdup (os_dev);
719 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
720 path[8] = 0;
721 return path;
723 #else
724 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
725 return xstrdup (os_dev);
726 #endif
729 static int
730 find_system_device (const char *os_dev)
732 int i;
733 char *os_disk;
735 os_disk = convert_system_partition_to_system_disk (os_dev);
736 if (! os_disk)
737 return -1;
739 for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
740 if (map[i].device && strcmp (map[i].device, os_disk) == 0)
742 free (os_disk);
743 return i;
746 free (os_disk);
747 return -1;
750 char *
751 grub_util_biosdisk_get_grub_dev (const char *os_dev)
753 struct stat st;
754 int drive;
756 if (stat (os_dev, &st) < 0)
758 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
759 return 0;
762 drive = find_system_device (os_dev);
763 if (drive < 0)
765 grub_error (GRUB_ERR_BAD_DEVICE,
766 "no mapping exists for `%s'", os_dev);
767 return 0;
770 if (! S_ISBLK (st.st_mode))
771 return make_device_name (drive, -1, -1);
773 #if defined(__linux__) || defined(__CYGWIN__)
774 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
775 partition, so mapping them to GRUB devices is not trivial.
776 Here, get the start sector of a partition by HDIO_GETGEO, and
777 compare it with each partition GRUB recognizes.
779 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
780 does not count the extended partition and missing primary
781 partitions. Use same method as on Linux here. */
783 char *name;
784 grub_disk_t disk;
785 int fd;
786 struct hd_geometry hdg;
787 int dos_part = -1;
788 int bsd_part = -1;
789 auto int find_partition (grub_disk_t disk,
790 const grub_partition_t partition);
792 int find_partition (grub_disk_t disk __attribute__ ((unused)),
793 const grub_partition_t partition)
795 struct grub_pc_partition *pcdata = NULL;
797 if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
798 pcdata = partition->data;
800 if (pcdata)
802 if (pcdata->bsd_part < 0)
803 grub_util_info ("DOS partition %d starts from %lu",
804 pcdata->dos_part, partition->start);
805 else
806 grub_util_info ("BSD partition %d,%c starts from %lu",
807 pcdata->dos_part, pcdata->bsd_part + 'a',
808 partition->start);
810 else
812 grub_util_info ("Partition %d starts from %lu",
813 partition->index, partition->start);
816 if (hdg.start == partition->start)
818 if (pcdata)
820 dos_part = pcdata->dos_part;
821 bsd_part = pcdata->bsd_part;
823 else
825 dos_part = partition->index;
826 bsd_part = -1;
828 return 1;
831 return 0;
834 name = make_device_name (drive, -1, -1);
836 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
837 return name;
839 fd = open (os_dev, O_RDONLY);
840 if (fd == -1)
842 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk geometry", os_dev);
843 free (name);
844 return 0;
847 if (ioctl (fd, HDIO_GETGEO, &hdg))
849 grub_error (GRUB_ERR_BAD_DEVICE,
850 "cannot get geometry of `%s'", os_dev);
851 close (fd);
852 free (name);
853 return 0;
856 close (fd);
858 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
860 if (hdg.start == 0)
861 return name;
863 grub_util_info ("opening the device %s", name);
864 disk = grub_disk_open (name);
865 free (name);
867 if (! disk)
868 return 0;
870 grub_partition_iterate (disk, find_partition);
871 if (grub_errno != GRUB_ERR_NONE)
873 grub_disk_close (disk);
874 return 0;
877 if (dos_part < 0)
879 grub_disk_close (disk);
880 grub_error (GRUB_ERR_BAD_DEVICE,
881 "cannot find the partition of `%s'", os_dev);
882 return 0;
885 return make_device_name (drive, dos_part, bsd_part);
888 #elif defined(__GNU__)
889 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
891 char *p;
892 int dos_part = -1;
893 int bsd_part = -1;
895 p = strrchr (os_dev, 's');
896 if (p)
898 long int n;
899 char *q;
901 p++;
902 n = strtol (p, &q, 10);
903 if (p != q && n != LONG_MIN && n != LONG_MAX)
905 dos_part = (int) n;
907 if (*q >= 'a' && *q <= 'g')
908 bsd_part = *q - 'a';
912 return make_device_name (drive, dos_part, bsd_part);
915 #else
916 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
917 return make_device_name (drive, -1, -1);
918 #endif