]> git.proxmox.com Git - grub2.git/blob - util/hostdisk.c
2009-12-07 Colin Watson <cjwatson@ubuntu.com>
[grub2.git] / util / hostdisk.c
1 /* hostdisk.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.
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/msdos_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>
28 #include <grub/i18n.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <limits.h>
41
42 #ifdef __linux__
43 # include <sys/ioctl.h> /* ioctl */
44 # if !defined(__GLIBC__) || \
45 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
46 /* Maybe libc doesn't have large file support. */
47 # include <linux/unistd.h> /* _llseek */
48 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
49 # ifndef BLKFLSBUF
50 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
51 # endif /* ! BLKFLSBUF */
52 # include <sys/ioctl.h> /* ioctl */
53 # ifndef HDIO_GETGEO
54 # define HDIO_GETGEO 0x0301 /* get device geometry */
55 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
56 defined. */
57 struct hd_geometry
58 {
59 unsigned char heads;
60 unsigned char sectors;
61 unsigned short cylinders;
62 unsigned long start;
63 };
64 # endif /* ! HDIO_GETGEO */
65 # ifndef BLKGETSIZE64
66 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
67 # endif /* ! BLKGETSIZE64 */
68 # ifndef MAJOR
69 # ifndef MINORBITS
70 # define MINORBITS 8
71 # endif /* ! MINORBITS */
72 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
73 # endif /* ! MAJOR */
74 # ifndef FLOPPY_MAJOR
75 # define FLOPPY_MAJOR 2
76 # endif /* ! FLOPPY_MAJOR */
77 # ifndef LOOP_MAJOR
78 # define LOOP_MAJOR 7
79 # endif /* ! LOOP_MAJOR */
80 #endif /* __linux__ */
81
82 #ifdef __CYGWIN__
83 # include <sys/ioctl.h>
84 # include <cygwin/fs.h> /* BLKGETSIZE64 */
85 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
86 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
87 # define FLOPPY_MAJOR 2
88 #endif
89
90 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
91 # include <sys/disk.h> /* DIOCGMEDIASIZE */
92 # include <sys/param.h>
93 # include <sys/sysctl.h>
94 #endif
95
96 #if defined(__APPLE__)
97 # include <sys/disk.h>
98 #endif
99
100 struct
101 {
102 char *drive;
103 char *device;
104 } map[256];
105
106 #ifdef __linux__
107 /* Check if we have devfs support. */
108 static int
109 have_devfs (void)
110 {
111 static int dev_devfsd_exists = -1;
112
113 if (dev_devfsd_exists < 0)
114 {
115 struct stat st;
116
117 dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
118 }
119
120 return dev_devfsd_exists;
121 }
122 #endif /* __linux__ */
123
124 static int
125 find_grub_drive (const char *name)
126 {
127 unsigned int i;
128
129 if (name)
130 {
131 for (i = 0; i < ARRAY_SIZE (map); i++)
132 if (map[i].drive && ! strcmp (map[i].drive, name))
133 return i;
134 }
135
136 return -1;
137 }
138
139 static int
140 find_free_slot ()
141 {
142 unsigned int i;
143
144 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
145 if (! map[i].drive)
146 return i;
147
148 return -1;
149 }
150
151 static int
152 grub_util_biosdisk_iterate (int (*hook) (const char *name))
153 {
154 unsigned i;
155
156 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
157 if (map[i].drive && hook (map[i].drive))
158 return 1;
159
160 return 0;
161 }
162
163 static grub_err_t
164 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
165 {
166 int drive;
167 struct stat st;
168
169 drive = find_grub_drive (name);
170 if (drive < 0)
171 return grub_error (GRUB_ERR_BAD_DEVICE,
172 "no mapping exists for `%s'", name);
173
174 disk->has_partitions = 1;
175 disk->id = drive;
176
177 /* Get the size. */
178 #if defined(__MINGW32__)
179 {
180 grub_uint64_t size;
181
182 size = grub_util_get_disk_size (map[drive].device);
183
184 if (size % 512)
185 grub_util_error ("unaligned device size");
186
187 disk->total_sectors = size >> 9;
188
189 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
190
191 return GRUB_ERR_NONE;
192 }
193 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
194 defined(__FreeBSD_kernel__) || defined(__APPLE__)
195 {
196 unsigned long long nr;
197 int fd;
198
199 fd = open (map[drive].device, O_RDONLY);
200 if (fd == -1)
201 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
202
203 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
204 if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
205 # else
206 if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
207 # endif
208 {
209 close (fd);
210 goto fail;
211 }
212
213 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
214 if (ioctl (fd, DIOCGMEDIASIZE, &nr))
215 # elif defined(__APPLE__)
216 if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
217 # else
218 if (ioctl (fd, BLKGETSIZE64, &nr))
219 # endif
220 {
221 close (fd);
222 goto fail;
223 }
224
225 close (fd);
226
227 #if defined (__APPLE__)
228 disk->total_sectors = nr;
229 #else
230 disk->total_sectors = nr / 512;
231
232 if (nr % 512)
233 grub_util_error ("unaligned device size");
234 #endif
235
236 grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
237
238 return GRUB_ERR_NONE;
239 }
240
241 fail:
242 /* In GNU/Hurd, stat() will return the right size. */
243 #elif !defined (__GNU__)
244 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
245 #endif
246 if (stat (map[drive].device, &st) < 0)
247 return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive].device);
248
249 disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
250
251 grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
252
253 return GRUB_ERR_NONE;
254 }
255
256 #ifdef __linux__
257 static int
258 linux_find_partition (char *dev, unsigned long sector)
259 {
260 size_t len = strlen (dev);
261 const char *format;
262 char *p;
263 int i;
264 char real_dev[PATH_MAX];
265
266 strcpy(real_dev, dev);
267
268 if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
269 {
270 p = real_dev + len - 4;
271 format = "part%d";
272 }
273 else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
274 {
275 p = real_dev + len;
276 format = "p%d";
277 }
278 else
279 {
280 p = real_dev + len;
281 format = "%d";
282 }
283
284 for (i = 1; i < 10000; i++)
285 {
286 int fd;
287 struct hd_geometry hdg;
288
289 sprintf (p, format, i);
290 fd = open (real_dev, O_RDONLY);
291 if (fd == -1)
292 return 0;
293
294 if (ioctl (fd, HDIO_GETGEO, &hdg))
295 {
296 close (fd);
297 return 0;
298 }
299
300 close (fd);
301
302 if (hdg.start == sector)
303 {
304 strcpy (dev, real_dev);
305 return 1;
306 }
307 }
308
309 return 0;
310 }
311 #endif /* __linux__ */
312
313 static int
314 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
315 {
316 int fd;
317
318 #ifdef O_LARGEFILE
319 flags |= O_LARGEFILE;
320 #endif
321 #ifdef O_SYNC
322 flags |= O_SYNC;
323 #endif
324 #ifdef O_FSYNC
325 flags |= O_FSYNC;
326 #endif
327 #ifdef O_BINARY
328 flags |= O_BINARY;
329 #endif
330
331 #ifdef __linux__
332 /* Linux has a bug that the disk cache for a whole disk is not consistent
333 with the one for a partition of the disk. */
334 {
335 int is_partition = 0;
336 char dev[PATH_MAX];
337
338 strcpy (dev, map[disk->id].device);
339 if (disk->partition && strncmp (map[disk->id].device, "/dev/", 5) == 0)
340 is_partition = linux_find_partition (dev, disk->partition->start);
341
342 /* Open the partition. */
343 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()", dev);
344 fd = open (dev, flags);
345 if (fd < 0)
346 {
347 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
348 return -1;
349 }
350
351 /* Make the buffer cache consistent with the physical disk. */
352 ioctl (fd, BLKFLSBUF, 0);
353
354 if (is_partition)
355 sector -= disk->partition->start;
356 }
357 #else /* ! __linux__ */
358 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
359 int sysctl_flags, sysctl_oldflags;
360 size_t sysctl_size = sizeof (sysctl_flags);
361
362 if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
363 {
364 grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
365 return -1;
366 }
367 sysctl_flags = sysctl_oldflags | 0x10;
368 if (! (sysctl_oldflags & 0x10)
369 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
370 {
371 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
372 return -1;
373 }
374 #endif
375
376 fd = open (map[disk->id].device, flags);
377
378 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
379 if (! (sysctl_oldflags & 0x10)
380 && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
381 {
382 grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
383 return -1;
384 }
385 #endif
386
387 #if defined(__APPLE__)
388 /* If we can't have exclusive access, try shared access */
389 if (fd < 0)
390 fd = open(map[disk->id].device, flags | O_SHLOCK);
391 #endif
392
393 if (fd < 0)
394 {
395 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
396 return -1;
397 }
398 #endif /* ! __linux__ */
399
400 #if defined(__linux__) && (!defined(__GLIBC__) || \
401 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
402 /* Maybe libc doesn't have large file support. */
403 {
404 loff_t offset, result;
405 static int _llseek (uint filedes, ulong hi, ulong lo,
406 loff_t *res, uint wh);
407 _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
408 loff_t *, res, uint, wh);
409
410 offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
411 if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
412 {
413 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
414 close (fd);
415 return -1;
416 }
417 }
418 #else
419 {
420 off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
421
422 if (lseek (fd, offset, SEEK_SET) != offset)
423 {
424 grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
425 close (fd);
426 return -1;
427 }
428 }
429 #endif
430
431 return fd;
432 }
433
434 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
435 error occurs, otherwise return LEN. */
436 static ssize_t
437 nread (int fd, char *buf, size_t len)
438 {
439 ssize_t size = len;
440
441 while (len)
442 {
443 ssize_t ret = read (fd, buf, len);
444
445 if (ret <= 0)
446 {
447 if (errno == EINTR)
448 continue;
449 else
450 return ret;
451 }
452
453 len -= ret;
454 buf += ret;
455 }
456
457 return size;
458 }
459
460 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
461 error occurs, otherwise return LEN. */
462 static ssize_t
463 nwrite (int fd, const char *buf, size_t len)
464 {
465 ssize_t size = len;
466
467 while (len)
468 {
469 ssize_t ret = write (fd, buf, len);
470
471 if (ret <= 0)
472 {
473 if (errno == EINTR)
474 continue;
475 else
476 return ret;
477 }
478
479 len -= ret;
480 buf += ret;
481 }
482
483 return size;
484 }
485
486 static grub_err_t
487 grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
488 grub_size_t size, char *buf)
489 {
490 int fd;
491
492 fd = open_device (disk, sector, O_RDONLY);
493 if (fd < 0)
494 return grub_errno;
495
496 #ifdef __linux__
497 if (sector == 0 && size > 1)
498 {
499 /* Work around a bug in Linux ez remapping. Linux remaps all
500 sectors that are read together with the MBR in one read. It
501 should only remap the MBR, so we split the read in two
502 parts. -jochen */
503 if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
504 {
505 grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
506 close (fd);
507 return grub_errno;
508 }
509
510 buf += GRUB_DISK_SECTOR_SIZE;
511 size--;
512 }
513 #endif /* __linux__ */
514
515 if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
516 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
517 grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
518
519 close (fd);
520 return grub_errno;
521 }
522
523 static grub_err_t
524 grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
525 grub_size_t size, const char *buf)
526 {
527 int fd;
528
529 fd = open_device (disk, sector, O_WRONLY);
530 if (fd < 0)
531 return grub_errno;
532
533 if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
534 != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
535 grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
536
537 close (fd);
538 return grub_errno;
539 }
540
541 static struct grub_disk_dev grub_util_biosdisk_dev =
542 {
543 .name = "biosdisk",
544 .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
545 .iterate = grub_util_biosdisk_iterate,
546 .open = grub_util_biosdisk_open,
547 .close = 0,
548 .read = grub_util_biosdisk_read,
549 .write = grub_util_biosdisk_write,
550 .next = 0
551 };
552
553 static void
554 read_device_map (const char *dev_map)
555 {
556 FILE *fp;
557 char buf[1024]; /* XXX */
558 int lineno = 0;
559 struct stat st;
560
561 auto void show_error (const char *msg);
562 void show_error (const char *msg)
563 {
564 grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
565 }
566
567 fp = fopen (dev_map, "r");
568 if (! fp)
569 {
570 grub_util_info (_("Cannot open `%s'"), dev_map);
571 return;
572 }
573
574 while (fgets (buf, sizeof (buf), fp))
575 {
576 char *p = buf;
577 char *e;
578 int drive;
579
580 lineno++;
581
582 /* Skip leading spaces. */
583 while (*p && isspace (*p))
584 p++;
585
586 /* If the first character is `#' or NUL, skip this line. */
587 if (*p == '\0' || *p == '#')
588 continue;
589
590 if (*p != '(')
591 show_error ("No open parenthesis found");
592
593 p++;
594 /* Find a free slot. */
595 drive = find_free_slot ();
596 if (drive < 0)
597 show_error ("Map table size exceeded");
598
599 e = p;
600 p = strchr (p, ')');
601 if (! p)
602 show_error ("No close parenthesis found");
603
604 map[drive].drive = xmalloc (p - e + sizeof ('\0'));
605 strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
606 map[drive].drive[p - e] = '\0';
607
608 p++;
609 /* Skip leading spaces. */
610 while (*p && isspace (*p))
611 p++;
612
613 if (*p == '\0')
614 show_error ("No filename found");
615
616 /* NUL-terminate the filename. */
617 e = p;
618 while (*e && ! isspace (*e))
619 e++;
620 *e = '\0';
621
622 #ifdef __MINGW32__
623 (void) st;
624 if (grub_util_get_disk_size (p) == -1LL)
625 #else
626 if (stat (p, &st) == -1)
627 #endif
628 {
629 free (map[drive].drive);
630 map[drive].drive = NULL;
631 grub_util_info ("Cannot stat `%s', skipping", p);
632 continue;
633 }
634
635 #ifdef __linux__
636 /* On Linux, the devfs uses symbolic links horribly, and that
637 confuses the interface very much, so use realpath to expand
638 symbolic links. */
639 map[drive].device = xmalloc (PATH_MAX);
640 if (! realpath (p, map[drive].device))
641 grub_util_error ("Cannot get the real path of `%s'", p);
642 #else
643 map[drive].device = xstrdup (p);
644 #endif
645 }
646
647 fclose (fp);
648 }
649
650 void
651 grub_util_biosdisk_init (const char *dev_map)
652 {
653 read_device_map (dev_map);
654 grub_disk_dev_register (&grub_util_biosdisk_dev);
655 }
656
657 void
658 grub_util_biosdisk_fini (void)
659 {
660 unsigned i;
661
662 for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
663 {
664 if (map[i].drive)
665 free (map[i].drive);
666 if (map[i].device)
667 free (map[i].device);
668 map[i].drive = map[i].device = NULL;
669 }
670
671 grub_disk_dev_unregister (&grub_util_biosdisk_dev);
672 }
673
674 static char *
675 make_device_name (int drive, int dos_part, int bsd_part)
676 {
677 char *ret;
678 char *dos_part_str = NULL;
679 char *bsd_part_str = NULL;
680
681 if (dos_part >= 0)
682 dos_part_str = xasprintf (",%d", dos_part + 1);
683
684 if (bsd_part >= 0)
685 bsd_part_str = xasprintf (",%c", dos_part + 'a');
686
687 ret = xasprintf ("%s%s%s", map[drive].drive,
688 dos_part_str ? : "",
689 bsd_part_str ? : "");
690
691 if (dos_part_str)
692 free (dos_part_str);
693
694 if (bsd_part_str)
695 free (bsd_part_str);
696
697 return ret;
698 }
699
700 static char *
701 convert_system_partition_to_system_disk (const char *os_dev)
702 {
703 #if defined(__linux__)
704 char *path = xmalloc (PATH_MAX);
705 if (! realpath (os_dev, path))
706 return NULL;
707
708 if (strncmp ("/dev/", path, 5) == 0)
709 {
710 char *p = path + 5;
711
712 /* If this is an IDE disk. */
713 if (strncmp ("ide/", p, 4) == 0)
714 {
715 p = strstr (p, "part");
716 if (p)
717 strcpy (p, "disc");
718
719 return path;
720 }
721
722 /* If this is a SCSI disk. */
723 if (strncmp ("scsi/", p, 5) == 0)
724 {
725 p = strstr (p, "part");
726 if (p)
727 strcpy (p, "disc");
728
729 return path;
730 }
731
732 /* If this is a DAC960 disk. */
733 if (strncmp ("rd/c", p, 4) == 0)
734 {
735 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
736 p = strchr (p, 'p');
737 if (p)
738 *p = '\0';
739
740 return path;
741 }
742
743 /* If this is a Mylex AcceleRAID Array. */
744 if (strncmp ("rs/c", p, 4) == 0)
745 {
746 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
747 p = strchr (p, 'p');
748 if (p)
749 *p = '\0';
750
751 return path;
752 }
753 /* If this is a CCISS disk. */
754 if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
755 {
756 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
757 p = strchr (p, 'p');
758 if (p)
759 *p = '\0';
760
761 return path;
762 }
763
764 /* If this is a Compaq Intelligent Drive Array. */
765 if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
766 {
767 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
768 p = strchr (p, 'p');
769 if (p)
770 *p = '\0';
771
772 return path;
773 }
774
775 /* If this is an I2O disk. */
776 if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
777 {
778 /* /dev/i2o/hd[a-z]([0-9]+)? */
779 p[sizeof ("i2o/hda") - 1] = '\0';
780 return path;
781 }
782
783 /* If this is a MultiMediaCard (MMC). */
784 if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
785 {
786 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
787 p = strchr (p, 'p');
788 if (p)
789 *p = '\0';
790
791 return path;
792 }
793
794 /* If this is an IDE, SCSI or Virtio disk. */
795 if (strncmp ("vdisk", p, 5) == 0
796 && p[5] >= 'a' && p[5] <= 'z')
797 {
798 /* /dev/vdisk[a-z][0-9]* */
799 p[6] = '\0';
800 return path;
801 }
802 if ((strncmp ("hd", p, 2) == 0
803 || strncmp ("vd", p, 2) == 0
804 || strncmp ("sd", p, 2) == 0)
805 && p[2] >= 'a' && p[2] <= 'z')
806 {
807 /* /dev/[hsv]d[a-z][0-9]* */
808 p[3] = '\0';
809 return path;
810 }
811
812 /* If this is a Xen virtual block device. */
813 if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
814 {
815 /* /dev/xvd[a-z][0-9]* */
816 p[4] = '\0';
817 return path;
818 }
819 }
820
821 return path;
822
823 #elif defined(__GNU__)
824 char *path = xstrdup (os_dev);
825 if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
826 {
827 char *p = strchr (path + 7, 's');
828 if (p)
829 *p = '\0';
830 }
831 return path;
832
833 #elif defined(__CYGWIN__)
834 char *path = xstrdup (os_dev);
835 if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
836 path[8] = 0;
837 return path;
838
839 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
840 char *path = xstrdup (os_dev);
841 if (strncmp ("/dev/", path, 5) == 0)
842 {
843 char *p;
844 for (p = path + 5; *p; ++p)
845 if (grub_isdigit(*p))
846 {
847 p = strchr (p, 's');
848 if (p)
849 *p = '\0';
850 break;
851 }
852 }
853 return path;
854
855 #else
856 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
857 return xstrdup (os_dev);
858 #endif
859 }
860
861 #if defined(__linux__) || defined(__CYGWIN__)
862 static int
863 device_is_wholedisk (const char *os_dev)
864 {
865 int len = strlen (os_dev);
866
867 if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
868 return 1;
869 return 0;
870 }
871 #endif
872
873 static int
874 find_system_device (const char *os_dev)
875 {
876 unsigned int i;
877 char *os_disk;
878
879 os_disk = convert_system_partition_to_system_disk (os_dev);
880 if (! os_disk)
881 return -1;
882
883 for (i = 0; i < ARRAY_SIZE (map); i++)
884 if (! map[i].device)
885 break;
886 else if (strcmp (map[i].device, os_disk) == 0)
887 {
888 free (os_disk);
889 return i;
890 }
891
892 if (i == ARRAY_SIZE (map))
893 grub_util_error (_("device count exceeds limit"));
894
895 map[i].device = os_disk;
896 map[i].drive = xstrdup (os_disk);
897
898 return i;
899 }
900
901 char *
902 grub_util_biosdisk_get_grub_dev (const char *os_dev)
903 {
904 struct stat st;
905 int drive;
906
907 if (stat (os_dev, &st) < 0)
908 {
909 grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
910 return 0;
911 }
912
913 drive = find_system_device (os_dev);
914 if (drive < 0)
915 {
916 grub_error (GRUB_ERR_BAD_DEVICE,
917 "no mapping exists for `%s'", os_dev);
918 return 0;
919 }
920
921 if (grub_strcmp (os_dev, convert_system_partition_to_system_disk (os_dev))
922 == 0)
923 return make_device_name (drive, -1, -1);
924
925 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
926 if (! S_ISCHR (st.st_mode))
927 #else
928 if (! S_ISBLK (st.st_mode))
929 #endif
930 return make_device_name (drive, -1, -1);
931
932 #if defined(__linux__) || defined(__CYGWIN__)
933 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
934 partition, so mapping them to GRUB devices is not trivial.
935 Here, get the start sector of a partition by HDIO_GETGEO, and
936 compare it with each partition GRUB recognizes.
937
938 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
939 does not count the extended partition and missing primary
940 partitions. Use same method as on Linux here. */
941 {
942 char *name;
943 grub_disk_t disk;
944 int fd;
945 struct hd_geometry hdg;
946 int dos_part = -1;
947 int bsd_part = -1;
948 auto int find_partition (grub_disk_t disk,
949 const grub_partition_t partition);
950
951 int find_partition (grub_disk_t disk __attribute__ ((unused)),
952 const grub_partition_t partition)
953 {
954 struct grub_msdos_partition *pcdata = NULL;
955
956 if (strcmp (partition->partmap->name, "part_msdos") == 0)
957 pcdata = partition->data;
958
959 if (pcdata)
960 {
961 if (pcdata->bsd_part < 0)
962 grub_util_info ("DOS partition %d starts from %lu",
963 pcdata->dos_part, partition->start);
964 else
965 grub_util_info ("BSD partition %d,%c starts from %lu",
966 pcdata->dos_part, pcdata->bsd_part + 'a',
967 partition->start);
968 }
969 else
970 {
971 grub_util_info ("Partition %d starts from %lu",
972 partition->index, partition->start);
973 }
974
975 if (hdg.start == partition->start)
976 {
977 if (pcdata)
978 {
979 dos_part = pcdata->dos_part;
980 bsd_part = pcdata->bsd_part;
981 }
982 else
983 {
984 dos_part = partition->index;
985 bsd_part = -1;
986 }
987 return 1;
988 }
989
990 return 0;
991 }
992
993 name = make_device_name (drive, -1, -1);
994
995 if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
996 return name;
997
998 fd = open (os_dev, O_RDONLY);
999 if (fd == -1)
1000 {
1001 grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' while attempting to get disk geometry", os_dev);
1002 free (name);
1003 return 0;
1004 }
1005
1006 if (ioctl (fd, HDIO_GETGEO, &hdg))
1007 {
1008 grub_error (GRUB_ERR_BAD_DEVICE,
1009 "cannot get geometry of `%s'", os_dev);
1010 close (fd);
1011 free (name);
1012 return 0;
1013 }
1014
1015 close (fd);
1016
1017 grub_util_info ("%s starts from %lu", os_dev, hdg.start);
1018
1019 if (hdg.start == 0 && device_is_wholedisk (os_dev))
1020 return name;
1021
1022 grub_util_info ("opening the device %s", name);
1023 disk = grub_disk_open (name);
1024 free (name);
1025
1026 if (! disk)
1027 return 0;
1028
1029 grub_partition_iterate (disk, find_partition);
1030 if (grub_errno != GRUB_ERR_NONE)
1031 {
1032 grub_disk_close (disk);
1033 return 0;
1034 }
1035
1036 if (dos_part < 0)
1037 {
1038 grub_disk_close (disk);
1039 grub_error (GRUB_ERR_BAD_DEVICE,
1040 "cannot find the partition of `%s'", os_dev);
1041 return 0;
1042 }
1043
1044 return make_device_name (drive, dos_part, bsd_part);
1045 }
1046
1047 #elif defined(__GNU__)
1048 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
1049 {
1050 char *p;
1051 int dos_part = -1;
1052 int bsd_part = -1;
1053
1054 p = strrchr (os_dev, 's');
1055 if (p)
1056 {
1057 long int n;
1058 char *q;
1059
1060 p++;
1061 n = strtol (p, &q, 10);
1062 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1063 {
1064 dos_part = (int) n - 1;
1065
1066 if (*q >= 'a' && *q <= 'g')
1067 bsd_part = *q - 'a';
1068 }
1069 }
1070
1071 return make_device_name (drive, dos_part, bsd_part);
1072 }
1073
1074 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
1075 /* FreeBSD uses "/dev/[a-z]+[0-9]+(s[0-9]+[a-z]?)?". */
1076 {
1077 int dos_part = -1;
1078 int bsd_part = -1;
1079
1080 if (strncmp ("/dev/", os_dev, 5) == 0)
1081 {
1082 const char *p;
1083 char *q;
1084 long int n;
1085
1086 for (p = os_dev + 5; *p; ++p)
1087 if (grub_isdigit(*p))
1088 {
1089 p = strchr (p, 's');
1090 if (p)
1091 {
1092 p++;
1093 n = strtol (p, &q, 10);
1094 if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
1095 {
1096 dos_part = (int) n - 1;
1097
1098 if (*q >= 'a' && *q <= 'g')
1099 bsd_part = *q - 'a';
1100 }
1101 }
1102 break;
1103 }
1104 }
1105
1106 return make_device_name (drive, dos_part, bsd_part);
1107 }
1108
1109 #else
1110 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1111 return make_device_name (drive, -1, -1);
1112 #endif
1113 }