]> git.proxmox.com Git - grub2.git/blob - util/biosdisk.c
2008-05-30 Pavel Roskin <proski@gnu.org>
[grub2.git] / util / biosdisk.c
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.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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>
27
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>
39
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
56 {
57 unsigned char heads;
58 unsigned char sectors;
59 unsigned short cylinders;
60 unsigned long start;
61 };
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__ */
79
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
87
88 struct
89 {
90 char *drive;
91 char *device;
92 } map[256];
93
94 #ifdef __linux__
95 /* Check if we have devfs support. */
96 static int
97 have_devfs (void)
98 {
99 static int dev_devfsd_exists = -1;
100
101 if (dev_devfsd_exists < 0)
102 {
103 struct stat st;
104
105 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
106 }
107
108 return dev_devfsd_exists;
109 }
110 #endif /* __linux__ */
111
112 static int
113 get_drive (const char *name)
114 {
115 unsigned int i;
116
117 if (name)
118 {
119 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
120 if (! strcmp (map[i].drive, name))
121 return i;
122 }
123 else
124 {
125 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
126 if (! map[i].drive)
127 return i;
128 }
129
130 return -1;
131 }
132
133 static int
134 grub_util_biosdisk_iterate (int (*hook) (const char *name))
135 {
136 unsigned i;
137
138 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
139 if (map[i].drive && hook (map[i].drive))
140 return 1;
141
142 return 0;
143 }
144
145 static grub_err_t
146 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
147 {
148 int drive;
149 struct stat st;
150
151 drive = get_drive (name);
152 if (drive < 0)
153 return grub_error (GRUB_ERR_BAD_DEVICE,
154 "no mapping exists for `%s'", name);
155
156 disk->has_partitions = 1;
157 disk->id = drive;
158
159 /* Get the size. */
160 #if defined(__linux__) || defined(__CYGWIN__)
161 {
162 unsigned long long nr;
163 int fd;
164
165 fd = open (map[drive].device, O_RDONLY);
166 if (fd == -1)
167 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[drive].device);
168
169 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
170 {
171 close (fd);
172 goto fail;
173 }
174
175 if (ioctl (fd, BLKGETSIZE64, &nr))
176 {
177 close (fd);
178 goto fail;
179 }
180
181 close (fd);
182 disk->total_sectors = nr / 512;
183
184 if (nr % 512)
185 grub_util_error ("unaligned device size");
186
187 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
188
189 return GRUB_ERR_NONE;
190 }
191
192 fail:
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."
196 #endif
197 if (stat (map[drive].device, &st) < 0)
198 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
199
200 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
201
202 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
203
204 return GRUB_ERR_NONE;
205 }
206
207 #ifdef __linux__
208 static int
209 linux_find_partition (char *dev, unsigned long sector)
210 {
211 size_t len = strlen (dev);
212 const char *format;
213 char *p;
214 int i;
215 char real_dev[PATH_MAX];
216
217 strcpy(real_dev, dev);
218
219 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
220 {
221 p = real_dev + len - 4;
222 format = "part%d";
223 }
224 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
225 {
226 p = real_dev + len;
227 format = "p%d";
228 }
229 else
230 {
231 p = real_dev + len;
232 format = "%d";
233 }
234
235 for (i = 1; i < 10000; i++)
236 {
237 int fd;
238 struct hd_geometry hdg;
239
240 sprintf (p, format, i);
241 fd = open (real_dev, O_RDONLY);
242 if (fd == -1)
243 return 0;
244
245 if (ioctl (fd, HDIO_GETGEO, &hdg))
246 {
247 close (fd);
248 return 0;
249 }
250
251 close (fd);
252
253 if (hdg.start == sector)
254 {
255 strcpy (dev, real_dev);
256 return 1;
257 }
258 }
259
260 return 0;
261 }
262 #endif /* __linux__ */
263
264 static int
265 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
266 {
267 int fd;
268
269 #ifdef O_LARGEFILE
270 flags |= O_LARGEFILE;
271 #endif
272 #ifdef O_SYNC
273 flags |= O_SYNC;
274 #endif
275 #ifdef O_FSYNC
276 flags |= O_FSYNC;
277 #endif
278
279 #ifdef __linux__
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. */
282 {
283 int is_partition = 0;
284 char dev[PATH_MAX];
285
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);
289
290 /* Open the partition. */
291 grub_util_info ("opening the device `%s'", dev);
292 fd = open (dev, flags);
293 if (fd < 0)
294 {
295 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
296 return -1;
297 }
298
299 /* Make the buffer cache consistent with the physical disk. */
300 ioctl (fd, BLKFLSBUF, 0);
301
302 if (is_partition)
303 sector -= disk->partition->start;
304 }
305 #else /* ! __linux__ */
306 fd = open (map[disk->id].device, flags);
307 if (fd < 0)
308 {
309 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[disk->id].device);
310 return -1;
311 }
312 #endif /* ! __linux__ */
313
314 #if defined(__linux__) && (!defined(__GLIBC__) || \
315 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
316 /* Maybe libc doesn't have large file support. */
317 {
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);
323
324 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
325 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
326 {
327 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
328 close (fd);
329 return -1;
330 }
331 }
332 #else
333 {
334 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
335
336 if (lseek (fd, offset, SEEK_SET) != offset)
337 {
338 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
339 close (fd);
340 return -1;
341 }
342 }
343 #endif
344
345 return fd;
346 }
347
348 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
349 error occurs, otherwise return LEN. */
350 static ssize_t
351 nread (int fd, char *buf, size_t len)
352 {
353 ssize_t size = len;
354
355 while (len)
356 {
357 ssize_t ret = read (fd, buf, len);
358
359 if (ret <= 0)
360 {
361 if (errno == EINTR)
362 continue;
363 else
364 return ret;
365 }
366
367 len -= ret;
368 buf += ret;
369 }
370
371 return size;
372 }
373
374 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
375 error occurs, otherwise return LEN. */
376 static ssize_t
377 nwrite (int fd, const char *buf, size_t len)
378 {
379 ssize_t size = len;
380
381 while (len)
382 {
383 ssize_t ret = write (fd, buf, len);
384
385 if (ret <= 0)
386 {
387 if (errno == EINTR)
388 continue;
389 else
390 return ret;
391 }
392
393 len -= ret;
394 buf += ret;
395 }
396
397 return size;
398 }
399
400 static grub_err_t
401 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
402 grub_size_t size, char *buf)
403 {
404 int fd;
405
406 fd = open_device (disk, sector, O_RDONLY);
407 if (fd < 0)
408 return grub_errno;
409
410 #ifdef __linux__
411 if (sector == 0 && size > 1)
412 {
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
416 parts. -jochen */
417 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
418 {
419 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
420 close (fd);
421 return grub_errno;
422 }
423
424 buf += GRUB_DISK_SECTOR_SIZE;
425 size--;
426 }
427 #endif /* __linux__ */
428
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);
432
433 close (fd);
434 return grub_errno;
435 }
436
437 static grub_err_t
438 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
439 grub_size_t size, const char *buf)
440 {
441 int fd;
442
443 fd = open_device (disk, sector, O_WRONLY);
444 if (fd < 0)
445 return grub_errno;
446
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);
450
451 close (fd);
452 return grub_errno;
453 }
454
455 static struct grub_disk_dev grub_util_biosdisk_dev =
456 {
457 .name = "biosdisk",
458 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
459 .iterate = grub_util_biosdisk_iterate,
460 .open = grub_util_biosdisk_open,
461 .close = 0,
462 .read = grub_util_biosdisk_read,
463 .write = grub_util_biosdisk_write,
464 .next = 0
465 };
466
467 static void
468 read_device_map (const char *dev_map)
469 {
470 FILE *fp;
471 char buf[1024]; /* XXX */
472 int lineno = 0;
473 struct stat st;
474
475 auto void show_error (const char *msg);
476 void show_error (const char *msg)
477 {
478 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
479 }
480
481 fp = fopen (dev_map, "r");
482 if (! fp)
483 grub_util_error ("Cannot open `%s'", dev_map);
484
485 while (fgets (buf, sizeof (buf), fp))
486 {
487 char *p = buf;
488 char *e;
489 int drive;
490
491 lineno++;
492
493 /* Skip leading spaces. */
494 while (*p && isspace (*p))
495 p++;
496
497 /* If the first character is `#' or NUL, skip this line. */
498 if (*p == '\0' || *p == '#')
499 continue;
500
501 if (*p != '(')
502 show_error ("No open parenthesis found");
503
504 p++;
505 /* Find a free slot. */
506 drive = get_drive (NULL);
507 if (drive < 0)
508 show_error ("Map table size exceeded");
509
510 e = p;
511 p = strchr (p, ')');
512 if (! p)
513 show_error ("No close parenthesis found");
514
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';
518
519 p++;
520 /* Skip leading spaces. */
521 while (*p && isspace (*p))
522 p++;
523
524 if (*p == '\0')
525 show_error ("No filename found");
526
527 /* NUL-terminate the filename. */
528 e = p;
529 while (*e && ! isspace (*e))
530 e++;
531 *e = '\0';
532
533 if (stat (p, &st) == -1)
534 {
535 grub_util_info ("Cannot stat `%s', skipping", p);
536 continue;
537 }
538
539 #ifdef __linux__
540 /* On Linux, the devfs uses symbolic links horribly, and that
541 confuses the interface very much, so use realpath to expand
542 symbolic links. */
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);
546 #else
547 map[drive].device = xstrdup (p);
548 #endif
549 }
550
551 fclose (fp);
552 }
553
554 void
555 grub_util_biosdisk_init (const char *dev_map)
556 {
557 read_device_map (dev_map);
558 grub_disk_dev_register (&grub_util_biosdisk_dev);
559 }
560
561 void
562 grub_util_biosdisk_fini (void)
563 {
564 unsigned i;
565
566 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
567 {
568 if (map[i].drive)
569 free (map[i].drive);
570 if (map[i].device)
571 free (map[i].device);
572 map[i].drive = map[i].device = NULL;
573 }
574
575 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
576 }
577
578 static char *
579 make_device_name (int drive, int dos_part, int bsd_part)
580 {
581 char *p;
582
583 p = xmalloc (30);
584 sprintf (p, "%s", map[drive].drive);
585
586 if (dos_part >= 0)
587 sprintf (p + strlen (p), ",%d", dos_part + 1);
588
589 if (bsd_part >= 0)
590 sprintf (p + strlen (p), ",%c", bsd_part + 'a');
591
592 return p;
593 }
594
595 static char *
596 get_os_disk (const char *os_dev)
597 {
598 #if defined(__linux__)
599 char *path = xmalloc (PATH_MAX);
600 if (! realpath (os_dev, path))
601 return 0;
602
603 if (strncmp ("/dev/", path, 5) == 0)
604 {
605 char *p = path + 5;
606
607 /* If this is an IDE disk. */
608 if (strncmp ("ide/", p, 4) == 0)
609 {
610 p = strstr (p, "part");
611 if (p)
612 strcpy (p, "disc");
613
614 return path;
615 }
616
617 /* If this is a SCSI disk. */
618 if (strncmp ("scsi/", p, 5) == 0)
619 {
620 p = strstr (p, "part");
621 if (p)
622 strcpy (p, "disc");
623
624 return path;
625 }
626
627 /* If this is a DAC960 disk. */
628 if (strncmp ("rd/c", p, 4) == 0)
629 {
630 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
631 p = strchr (p, 'p');
632 if (p)
633 *p = '\0';
634
635 return path;
636 }
637
638 /* If this is a CCISS disk. */
639 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
640 {
641 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
642 p = strchr (p, 'p');
643 if (p)
644 *p = '\0';
645
646 return path;
647 }
648
649 /* If this is a MultiMediaCard (MMC). */
650 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
651 {
652 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
653 p = strchr (p, 'p');
654 if (p)
655 *p = '\0';
656
657 return path;
658 }
659
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')
665 {
666 /* /dev/[hsv]d[a-z][0-9]* */
667 p[3] = '\0';
668 return path;
669 }
670
671 /* If this is a Xen virtual block device. */
672 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
673 {
674 /* /dev/xvd[a-z][0-9]* */
675 p[4] = '\0';
676 return path;
677 }
678 }
679
680 return path;
681
682 #elif defined(__GNU__)
683 char *path = xstrdup (os_dev);
684 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
685 {
686 char *p = strchr (path + 7, 's');
687 if (p)
688 *p = '\0';
689 }
690 return path;
691
692 #elif defined(__CYGWIN__)
693 char *path = xstrdup (os_dev);
694 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
695 path[8] = 0;
696 return path;
697
698 #else
699 # warning "The function `get_os_disk' might not work on your OS correctly."
700 return xstrdup (os_dev);
701 #endif
702 }
703
704 static int
705 find_drive (const char *os_dev)
706 {
707 int i;
708 char *os_disk;
709
710 os_disk = get_os_disk (os_dev);
711 if (! os_disk)
712 return -1;
713
714 for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
715 if (map[i].device && strcmp (map[i].device, os_disk) == 0)
716 {
717 free (os_disk);
718 return i;
719 }
720
721 free (os_disk);
722 return -1;
723 }
724
725 char *
726 grub_util_biosdisk_get_grub_dev (const char *os_dev)
727 {
728 struct stat st;
729 int drive;
730
731 if (stat (os_dev, &st) < 0)
732 {
733 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
734 return 0;
735 }
736
737 drive = find_drive (os_dev);
738 if (drive < 0)
739 {
740 grub_error (GRUB_ERR_BAD_DEVICE,
741 "no mapping exists for `%s'", os_dev);
742 return 0;
743 }
744
745 if (! S_ISBLK (st.st_mode))
746 return make_device_name (drive, -1, -1);
747
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.
753
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. */
757 {
758 char *name;
759 grub_disk_t disk;
760 int fd;
761 struct hd_geometry hdg;
762 int dos_part = -1;
763 int bsd_part = -1;
764 auto int find_partition (grub_disk_t disk,
765 const grub_partition_t partition);
766
767 int find_partition (grub_disk_t disk __attribute__ ((unused)),
768 const grub_partition_t partition)
769 {
770 struct grub_pc_partition *pcdata = NULL;
771
772 if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
773 pcdata = partition->data;
774
775 if (pcdata)
776 {
777 if (pcdata->bsd_part < 0)
778 grub_util_info ("DOS partition %d starts from %lu",
779 pcdata->dos_part, partition->start);
780 else
781 grub_util_info ("BSD partition %d,%c starts from %lu",
782 pcdata->dos_part, pcdata->bsd_part + 'a',
783 partition->start);
784 }
785 else
786 {
787 grub_util_info ("Partition %d starts from %lu",
788 partition->index, partition->start);
789 }
790
791 if (hdg.start == partition->start)
792 {
793 if (pcdata)
794 {
795 dos_part = pcdata->dos_part;
796 bsd_part = pcdata->bsd_part;
797 }
798 else
799 {
800 dos_part = partition->index;
801 bsd_part = -1;
802 }
803 return 1;
804 }
805
806 return 0;
807 }
808
809 name = make_device_name (drive, -1, -1);
810
811 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
812 return name;
813
814 fd = open (os_dev, O_RDONLY);
815 if (fd == -1)
816 {
817 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", os_dev);
818 free (name);
819 return 0;
820 }
821
822 if (ioctl (fd, HDIO_GETGEO, &hdg))
823 {
824 grub_error (GRUB_ERR_BAD_DEVICE,
825 "cannot get geometry of `%s'", os_dev);
826 close (fd);
827 free (name);
828 return 0;
829 }
830
831 close (fd);
832
833 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
834
835 if (hdg.start == 0)
836 return name;
837
838 grub_util_info ("opening the device %s", name);
839 disk = grub_disk_open (name);
840 free (name);
841
842 if (! disk)
843 return 0;
844
845 if (grub_partition_iterate (disk, find_partition) != GRUB_ERR_NONE)
846 {
847 grub_disk_close (disk);
848 return 0;
849 }
850
851 if (dos_part < 0)
852 {
853 grub_disk_close (disk);
854 grub_error (GRUB_ERR_BAD_DEVICE,
855 "cannot find the partition of `%s'", os_dev);
856 return 0;
857 }
858
859 return make_device_name (drive, dos_part, bsd_part);
860 }
861
862 #elif defined(__GNU__)
863 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
864 {
865 char *p;
866 int dos_part = -1;
867 int bsd_part = -1;
868
869 p = strrchr (os_dev, 's');
870 if (p)
871 {
872 long int n;
873 char *q;
874
875 p++;
876 n = strtol (p, &q, 10);
877 if (p != q && n != LONG_MIN && n != LONG_MAX)
878 {
879 dos_part = (int) n;
880
881 if (*q >= 'a' && *q <= 'g')
882 bsd_part = *q - 'a';
883 }
884 }
885
886 return make_device_name (drive, dos_part, bsd_part);
887 }
888
889 #else
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);
892 #endif
893 }