1 /* hostdisk.c - emulate biosdisk */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010 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/msdos_partition.h>
23 #include <grub/types.h>
25 #include <grub/emu/misc.h>
26 #include <grub/emu/hostdisk.h>
27 #include <grub/emu/getroot.h>
28 #include <grub/misc.h>
29 #include <grub/i18n.h>
30 #include <grub/list.h>
38 #include <sys/types.h>
45 # include <sys/ioctl.h> /* ioctl */
46 # if !defined(__GLIBC__) || \
47 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
48 /* Maybe libc doesn't have large file support. */
49 # include <linux/unistd.h> /* _llseek */
50 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
52 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
53 # endif /* ! BLKFLSBUF */
54 # include <sys/ioctl.h> /* ioctl */
56 # define HDIO_GETGEO 0x0301 /* get device geometry */
57 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
62 unsigned char sectors
;
63 unsigned short cylinders
;
66 # endif /* ! HDIO_GETGEO */
68 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
69 # endif /* ! BLKGETSIZE64 */
73 # endif /* ! MINORBITS */
74 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
77 # define FLOPPY_MAJOR 2
78 # endif /* ! FLOPPY_MAJOR */
81 # endif /* ! LOOP_MAJOR */
82 #endif /* __linux__ */
85 # include <sys/ioctl.h>
86 # include <cygwin/fs.h> /* BLKGETSIZE64 */
87 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
88 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
89 # define FLOPPY_MAJOR 2
92 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
93 # include <sys/disk.h> /* DIOCGMEDIASIZE */
94 # include <sys/param.h>
95 # include <sys/sysctl.h>
96 # define MAJOR(dev) major(dev)
97 # define FLOPPY_MAJOR 2
100 #if defined(__APPLE__)
101 # include <sys/disk.h>
104 #ifdef HAVE_DEVICE_MAPPER
105 # include <libdevmapper.h>
108 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
110 #elif defined(__NetBSD__)
111 # define HAVE_DIOCGDINFO
112 # include <sys/ioctl.h>
113 # include <sys/disklabel.h> /* struct disklabel */
114 #else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
115 # undef HAVE_DIOCGDINFO
116 #endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
118 #if defined(__NetBSD__)
119 # ifdef HAVE_GETRAWPARTITION
120 # include <util.h> /* getrawpartition */
121 # endif /* HAVE_GETRAWPARTITION */
122 # include <sys/fdio.h>
123 # ifndef FLOPPY_MAJOR
124 # define FLOPPY_MAJOR 2
125 # endif /* ! FLOPPY_MAJOR */
126 # ifndef RAW_FLOPPY_MAJOR
127 # define RAW_FLOPPY_MAJOR 9
128 # endif /* ! RAW_FLOPPY_MAJOR */
129 #endif /* defined(__NetBSD__) */
138 struct grub_util_biosdisk_data
148 /* Check if we have devfs support. */
152 static int dev_devfsd_exists
= -1;
154 if (dev_devfsd_exists
< 0)
158 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
161 return dev_devfsd_exists
;
163 #endif /* __linux__ */
165 #if defined(__NetBSD__)
166 /* Adjust device driver parameters. This function should be called just
167 after successfully opening the device. For now, it simply prevents the
168 floppy driver from retrying operations on failure, as otherwise the
169 driver takes a while to abort when there is no floppy in the drive. */
171 configure_device_driver (int fd
)
175 if (fstat (fd
, &st
) < 0 || ! S_ISCHR (st
.st_mode
))
177 if (major(st
.st_rdev
) == RAW_FLOPPY_MAJOR
)
181 if (ioctl (fd
, FDIOCGETOPTS
, &floppy_opts
) == -1)
183 floppy_opts
|= FDOPT_NORETRY
;
184 if (ioctl (fd
, FDIOCSETOPTS
, &floppy_opts
) == -1)
188 #endif /* defined(__NetBSD__) */
191 find_grub_drive (const char *name
)
197 for (i
= 0; i
< ARRAY_SIZE (map
); i
++)
198 if (map
[i
].drive
&& ! strcmp (map
[i
].drive
, name
))
206 find_free_slot (void)
210 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
218 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
222 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
223 if (map
[i
].drive
&& hook (map
[i
].drive
))
230 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
234 struct grub_util_biosdisk_data
*data
;
236 drive
= find_grub_drive (name
);
238 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
239 "no mapping exists for `%s'", name
);
242 disk
->data
= data
= xmalloc (sizeof (struct grub_util_biosdisk_data
));
244 data
->access_mode
= 0;
247 data
->device_map
= map
[drive
].device_map
;
250 #if defined(__MINGW32__)
254 size
= grub_util_get_disk_size (map
[drive
].device
);
257 grub_util_error ("unaligned device size");
259 disk
->total_sectors
= size
>> 9;
261 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
263 return GRUB_ERR_NONE
;
265 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
266 defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
268 # if defined(__NetBSD__)
269 struct disklabel label
;
271 unsigned long long nr
;
275 fd
= open (map
[drive
].device
, O_RDONLY
);
277 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "cannot open `%s' while attempting to get disk size", map
[drive
].device
);
279 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
280 if (fstat (fd
, &st
) < 0 || ! S_ISCHR (st
.st_mode
))
282 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
290 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
291 if (ioctl (fd
, DIOCGMEDIASIZE
, &nr
))
292 # elif defined(__APPLE__)
293 if (ioctl (fd
, DKIOCGETBLOCKCOUNT
, &nr
))
294 # elif defined(__NetBSD__)
295 configure_device_driver (fd
);
296 if (ioctl (fd
, DIOCGDINFO
, &label
) == -1)
298 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
307 # if defined (__APPLE__)
308 disk
->total_sectors
= nr
;
309 # elif defined(__NetBSD__)
310 disk
->total_sectors
= label
.d_secperunit
;
312 disk
->total_sectors
= nr
/ 512;
315 grub_util_error ("unaligned device size");
318 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
320 return GRUB_ERR_NONE
;
324 /* In GNU/Hurd, stat() will return the right size. */
325 #elif !defined (__GNU__)
326 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
328 if (stat (map
[drive
].device
, &st
) < 0)
329 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "cannot stat `%s'", map
[drive
].device
);
331 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
333 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
335 return GRUB_ERR_NONE
;
339 grub_util_device_is_mapped (const char *dev
)
341 #ifdef HAVE_DEVICE_MAPPER
344 if (!grub_device_mapper_supported ())
347 if (stat (dev
, &st
) < 0)
350 return dm_is_dm_major (major (st
.st_rdev
));
353 #endif /* HAVE_DEVICE_MAPPER */
356 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
357 /* FIXME: geom actually gives us the whole container hierarchy.
358 It can be used more efficiently than this. */
360 follow_geom_up (const char *name
, grub_disk_addr_t
*off_out
, char **name_out
)
363 struct gclass
*class;
367 grub_util_info ("following geom '%s'", name
);
369 error
= geom_gettree (&mesh
);
371 grub_util_error ("couldn't open geom");
373 LIST_FOREACH (class, &mesh
.lg_class
, lg_class
)
374 if (strcasecmp (class->lg_name
, "part") == 0)
377 grub_util_error ("couldn't open geom part");
379 LIST_FOREACH (geom
, &class->lg_geom
, lg_geom
)
381 struct gprovider
*provider
;
382 LIST_FOREACH (provider
, &geom
->lg_provider
, lg_provider
)
383 if (strcmp (provider
->lg_name
, name
) == 0)
385 char *name_tmp
= xstrdup (geom
->lg_name
);
386 grub_disk_addr_t off
= 0;
387 struct gconfig
*config
;
388 grub_util_info ("geom '%s' has parent '%s'", name
, geom
->lg_name
);
390 follow_geom_up (name_tmp
, &off
, name_out
);
392 LIST_FOREACH (config
, &provider
->lg_config
, lg_config
)
393 if (strcasecmp (config
->lg_name
, "start") == 0)
394 off
+= strtoull (config
->lg_val
, 0, 10);
400 grub_util_info ("geom '%s' has no parent", name
);
402 *name_out
= xstrdup (name
);
407 static grub_disk_addr_t
408 find_partition_start (const char *dev
)
410 grub_disk_addr_t out
;
411 if (strncmp (dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
413 follow_geom_up (dev
+ sizeof ("/dev/") - 1, &out
, NULL
);
417 #elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO)
418 static grub_disk_addr_t
419 find_partition_start (const char *dev
)
422 # if !defined(HAVE_DIOCGDINFO)
423 struct hd_geometry hdg
;
424 # else /* defined(HAVE_DIOCGDINFO) */
425 struct disklabel label
;
427 # endif /* !defined(HAVE_DIOCGDINFO) */
429 # ifdef HAVE_DEVICE_MAPPER
430 if (grub_util_device_is_mapped (dev
)) {
431 struct dm_task
*task
= NULL
;
432 grub_uint64_t start
, length
;
433 char *target_type
, *params
, *space
;
434 grub_disk_addr_t partition_start
;
436 /* If any device-mapper operation fails, we fall back silently to
438 task
= dm_task_create (DM_DEVICE_TABLE
);
441 grub_dprintf ("hostdisk", "dm_task_create failed\n");
445 if (! dm_task_set_name (task
, dev
))
447 grub_dprintf ("hostdisk", "dm_task_set_name failed\n");
451 if (! dm_task_run (task
))
453 grub_dprintf ("hostdisk", "dm_task_run failed\n");
457 dm_get_next_target (task
, NULL
, &start
, &length
, &target_type
, ¶ms
);
460 grub_dprintf ("hostdisk", "no dm target\n");
463 if (strcmp (target_type
, "linear") != 0)
465 grub_dprintf ("hostdisk", "ignoring dm target %s (not linear)\n",
471 grub_dprintf ("hostdisk", "no dm params\n");
475 /* The params string for a linear target looks like this:
476 DEVICE-NAME START-SECTOR
478 space
= strchr (params
, ' ');
482 partition_start
= strtoull (space
+ 1, NULL
, 10);
485 grub_dprintf ("hostdisk", "dm %s starts at %llu\n",
486 dev
, (unsigned long long) partition_start
);
487 dm_task_destroy (task
);
488 return partition_start
;
493 dm_task_destroy (task
);
495 # endif /* HAVE_DEVICE_MAPPER */
497 fd
= open (dev
, O_RDONLY
);
500 grub_error (GRUB_ERR_BAD_DEVICE
,
501 # if !defined(HAVE_DIOCGDINFO)
502 "cannot open `%s' while attempting to get disk geometry", dev
);
503 # else /* defined(HAVE_DIOCGDINFO) */
504 "cannot open `%s' while attempting to get disk label", dev
);
505 # endif /* !defined(HAVE_DIOCGDINFO) */
509 # if !defined(HAVE_DIOCGDINFO)
510 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
511 # else /* defined(HAVE_DIOCGDINFO) */
512 # if defined(__NetBSD__)
513 configure_device_driver (fd
);
514 # endif /* defined(__NetBSD__) */
515 if (ioctl (fd
, DIOCGDINFO
, &label
) == -1)
516 # endif /* !defined(HAVE_DIOCGDINFO) */
518 grub_error (GRUB_ERR_BAD_DEVICE
,
519 # if !defined(HAVE_DIOCGDINFO)
520 "cannot get disk geometry of `%s'", dev
);
521 # else /* defined(HAVE_DIOCGDINFO) */
522 "cannot get disk label of `%s'", dev
);
523 # endif /* !defined(HAVE_DIOCGDINFO) */
530 # if !defined(HAVE_DIOCGDINFO)
532 # else /* defined(HAVE_DIOCGDINFO) */
534 p_index
= dev
[strlen(dev
) - 1] - 'a';
538 if (p_index
>= label
.d_npartitions
|| p_index
< 0)
540 grub_error (GRUB_ERR_BAD_DEVICE
,
541 "no disk label entry for `%s'", dev
);
544 return (grub_disk_addr_t
) label
.d_partitions
[p_index
].p_offset
;
545 # endif /* !defined(HAVE_DIOCGDINFO) */
547 #endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */
550 /* Cache of partition start sectors for each disk. */
551 struct linux_partition_cache
553 struct linux_partition_cache
*next
;
559 struct linux_partition_cache
*linux_partition_cache_list
;
562 linux_find_partition (char *dev
, grub_disk_addr_t sector
)
564 size_t len
= strlen (dev
);
568 char real_dev
[PATH_MAX
];
569 struct linux_partition_cache
*cache
;
571 strcpy(real_dev
, dev
);
573 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
575 p
= real_dev
+ len
- 4;
578 else if (real_dev
[len
- 1] >= '0' && real_dev
[len
- 1] <= '9')
589 for (cache
= linux_partition_cache_list
; cache
; cache
= cache
->next
)
591 if (strcmp (cache
->dev
, dev
) == 0 && cache
->start
== sector
)
593 sprintf (p
, format
, cache
->partno
);
594 strcpy (dev
, real_dev
);
599 for (i
= 1; i
< 10000; i
++)
602 grub_disk_addr_t start
;
604 sprintf (p
, format
, i
);
606 fd
= open (real_dev
, O_RDONLY
);
611 start
= find_partition_start (real_dev
);
612 /* We don't care about errors here. */
613 grub_errno
= GRUB_ERR_NONE
;
617 struct linux_partition_cache
*new_cache_item
;
619 new_cache_item
= xmalloc (sizeof *new_cache_item
);
620 new_cache_item
->dev
= xstrdup (dev
);
621 new_cache_item
->start
= start
;
622 new_cache_item
->partno
= i
;
623 grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list
),
624 GRUB_AS_LIST (new_cache_item
));
626 strcpy (dev
, real_dev
);
633 #endif /* __linux__ */
636 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
639 struct grub_util_biosdisk_data
*data
= disk
->data
;
642 flags
|= O_LARGEFILE
;
655 /* Linux has a bug that the disk cache for a whole disk is not consistent
656 with the one for a partition of the disk. */
658 int is_partition
= 0;
660 grub_disk_addr_t part_start
= 0;
662 part_start
= grub_partition_get_start (disk
->partition
);
664 strcpy (dev
, map
[disk
->id
].device
);
665 if (disk
->partition
&& sector
>= part_start
666 && strncmp (map
[disk
->id
].device
, "/dev/", 5) == 0)
667 is_partition
= linux_find_partition (dev
, part_start
);
669 if (data
->dev
&& strcmp (data
->dev
, dev
) == 0 &&
670 data
->access_mode
== (flags
& O_ACCMODE
))
672 grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev
);
680 if (data
->access_mode
== O_RDWR
|| data
->access_mode
== O_WRONLY
)
685 ioctl (data
->fd
, BLKFLSBUF
, 0);
693 /* Open the partition. */
694 grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev
);
695 fd
= open (dev
, flags
);
698 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
702 data
->dev
= xstrdup (dev
);
703 data
->access_mode
= (flags
& O_ACCMODE
);
708 sector
-= part_start
;
710 #else /* ! __linux__ */
711 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
712 int sysctl_flags
, sysctl_oldflags
;
713 size_t sysctl_size
= sizeof (sysctl_flags
);
715 if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags
, &sysctl_size
, NULL
, 0))
717 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot get current flags of sysctl kern.geom.debugflags");
720 sysctl_flags
= sysctl_oldflags
| 0x10;
721 if (! (sysctl_oldflags
& 0x10)
722 && sysctlbyname ("kern.geom.debugflags", NULL
, 0, &sysctl_flags
, sysctl_size
))
724 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot set flags of sysctl kern.geom.debugflags");
729 if (data
->dev
&& strcmp (data
->dev
, map
[disk
->id
].device
) == 0 &&
730 data
->access_mode
== (flags
& O_ACCMODE
))
732 grub_dprintf ("hostdisk", "reusing open device `%s'\n", data
->dev
);
740 if (data
->access_mode
== O_RDWR
|| data
->access_mode
== O_WRONLY
)
745 ioctl (data
->fd
, BLKFLSBUF
, 0);
752 fd
= open (map
[disk
->id
].device
, flags
);
755 data
->dev
= xstrdup (map
[disk
->id
].device
);
756 data
->access_mode
= (flags
& O_ACCMODE
);
761 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
762 if (! (sysctl_oldflags
& 0x10)
763 && sysctlbyname ("kern.geom.debugflags", NULL
, 0, &sysctl_oldflags
, sysctl_size
))
765 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
770 #if defined(__APPLE__)
771 /* If we can't have exclusive access, try shared access */
773 fd
= open(map
[disk
->id
].device
, flags
| O_SHLOCK
);
778 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' in open_device()", map
[disk
->id
].device
);
781 #endif /* ! __linux__ */
783 #if defined(__NetBSD__)
784 configure_device_driver (fd
);
785 #endif /* defined(__NetBSD__) */
787 #if defined(__linux__) && (!defined(__GLIBC__) || \
788 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
789 /* Maybe libc doesn't have large file support. */
791 loff_t offset
, result
;
792 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
793 loff_t
*res
, uint wh
);
794 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
795 loff_t
*, res
, uint
, wh
);
797 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
798 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
800 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
807 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
809 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
811 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
821 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
822 error occurs, otherwise return LEN. */
824 nread (int fd
, char *buf
, size_t len
)
830 ssize_t ret
= read (fd
, buf
, len
);
847 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
848 error occurs, otherwise return LEN. */
850 nwrite (int fd
, const char *buf
, size_t len
)
856 ssize_t ret
= write (fd
, buf
, len
);
874 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
875 grub_size_t size
, char *buf
)
879 /* Split pre-partition and partition reads. */
880 if (disk
->partition
&& sector
< disk
->partition
->start
881 && sector
+ size
> disk
->partition
->start
)
884 err
= grub_util_biosdisk_read (disk
, sector
,
885 disk
->partition
->start
- sector
,
890 return grub_util_biosdisk_read (disk
, disk
->partition
->start
,
891 size
- (disk
->partition
->start
- sector
),
892 buf
+ ((disk
->partition
->start
- sector
)
893 << GRUB_DISK_SECTOR_BITS
));
896 fd
= open_device (disk
, sector
, O_RDONLY
);
901 if (sector
== 0 && size
> 1)
903 /* Work around a bug in Linux ez remapping. Linux remaps all
904 sectors that are read together with the MBR in one read. It
905 should only remap the MBR, so we split the read in two
907 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
909 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
].device
);
913 buf
+= GRUB_DISK_SECTOR_SIZE
;
916 #endif /* __linux__ */
918 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
919 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
920 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
].device
);
926 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
927 grub_size_t size
, const char *buf
)
931 /* Split pre-partition and partition writes. */
932 if (disk
->partition
&& sector
< disk
->partition
->start
933 && sector
+ size
> disk
->partition
->start
)
936 err
= grub_util_biosdisk_write (disk
, sector
,
937 disk
->partition
->start
- sector
,
942 return grub_util_biosdisk_write (disk
, disk
->partition
->start
,
943 size
- (disk
->partition
->start
- sector
),
944 buf
+ ((disk
->partition
->start
- sector
)
945 << GRUB_DISK_SECTOR_BITS
));
948 fd
= open_device (disk
, sector
, O_WRONLY
);
952 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
953 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
954 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
].device
);
960 grub_util_biosdisk_flush (struct grub_disk
*disk
)
962 struct grub_util_biosdisk_data
*data
= disk
->data
;
964 if (disk
->dev
->id
!= GRUB_DISK_DEVICE_BIOSDISK_ID
)
965 return GRUB_ERR_NONE
;
968 data
->fd
= open_device (disk
, 0, O_RDONLY
);
975 ioctl (data
->fd
, BLKFLSBUF
, 0);
977 return GRUB_ERR_NONE
;
981 grub_util_biosdisk_close (struct grub_disk
*disk
)
983 struct grub_util_biosdisk_data
*data
= disk
->data
;
988 if (data
->access_mode
== O_RDWR
|| data
->access_mode
== O_WRONLY
)
989 grub_util_biosdisk_flush (disk
);
995 static struct grub_disk_dev grub_util_biosdisk_dev
=
998 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
999 .iterate
= grub_util_biosdisk_iterate
,
1000 .open
= grub_util_biosdisk_open
,
1001 .close
= grub_util_biosdisk_close
,
1002 .read
= grub_util_biosdisk_read
,
1003 .write
= grub_util_biosdisk_write
,
1008 read_device_map (const char *dev_map
)
1011 char buf
[1024]; /* XXX */
1015 auto void show_error (const char *msg
);
1016 void show_error (const char *msg
)
1018 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
1021 if (dev_map
[0] == '\0')
1023 grub_util_info (_("no device.map"));
1027 fp
= fopen (dev_map
, "r");
1030 grub_util_info (_("cannot open `%s'"), dev_map
);
1034 while (fgets (buf
, sizeof (buf
), fp
))
1042 /* Skip leading spaces. */
1043 while (*p
&& isspace (*p
))
1046 /* If the first character is `#' or NUL, skip this line. */
1047 if (*p
== '\0' || *p
== '#')
1051 show_error ("No open parenthesis found");
1054 /* Find a free slot. */
1055 drive
= find_free_slot ();
1057 show_error ("Map table size exceeded");
1060 p
= strchr (p
, ')');
1062 show_error ("No close parenthesis found");
1064 map
[drive
].drive
= xmalloc (p
- e
+ sizeof ('\0'));
1065 strncpy (map
[drive
].drive
, e
, p
- e
+ sizeof ('\0'));
1066 map
[drive
].drive
[p
- e
] = '\0';
1067 map
[drive
].device_map
= 1;
1070 /* Skip leading spaces. */
1071 while (*p
&& isspace (*p
))
1075 show_error ("No filename found");
1077 /* NUL-terminate the filename. */
1079 while (*e
&& ! isspace (*e
))
1085 if (grub_util_get_disk_size (p
) == -1LL)
1087 if (stat (p
, &st
) == -1)
1090 free (map
[drive
].drive
);
1091 map
[drive
].drive
= NULL
;
1092 grub_util_info ("Cannot stat `%s', skipping", p
);
1097 /* On Linux, the devfs uses symbolic links horribly, and that
1098 confuses the interface very much, so use realpath to expand
1099 symbolic links. Leave /dev/mapper/ alone, though. */
1100 if (strncmp (p
, "/dev/mapper/", 12) != 0)
1102 map
[drive
].device
= xmalloc (PATH_MAX
);
1103 if (! realpath (p
, map
[drive
].device
))
1104 grub_util_error ("cannot get the real path of `%s'", p
);
1108 map
[drive
].device
= xstrdup (p
);
1115 grub_util_biosdisk_init (const char *dev_map
)
1117 read_device_map (dev_map
);
1118 grub_disk_dev_register (&grub_util_biosdisk_dev
);
1122 grub_util_biosdisk_fini (void)
1126 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
1129 free (map
[i
].drive
);
1131 free (map
[i
].device
);
1132 map
[i
].drive
= map
[i
].device
= NULL
;
1135 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
1139 * Note: we do not use the new partition naming scheme as dos_part does not
1140 * necessarily correspond to an msdos partition.
1143 make_device_name (int drive
, int dos_part
, int bsd_part
)
1146 char *dos_part_str
= NULL
;
1147 char *bsd_part_str
= NULL
;
1150 dos_part_str
= xasprintf (",%d", dos_part
+ 1);
1153 bsd_part_str
= xasprintf (",%d", bsd_part
+ 1);
1155 ret
= xasprintf ("%s%s%s", map
[drive
].drive
,
1156 dos_part_str
? : "",
1157 bsd_part_str
? : "");
1160 free (dos_part_str
);
1163 free (bsd_part_str
);
1168 #ifdef HAVE_DEVICE_MAPPER
1170 grub_util_get_dm_node_linear_info (const char *dev
,
1173 struct dm_task
*dmt
;
1175 uint64_t length
, start
;
1176 char *target
, *params
;
1180 dmt
= dm_task_create(DM_DEVICE_TABLE
);
1184 if (!dm_task_set_name(dmt
, dev
))
1186 dm_task_no_open_count(dmt
);
1187 if (!dm_task_run(dmt
))
1189 next
= dm_get_next_target(dmt
, next
, &start
, &length
,
1191 if (grub_strcmp (target
, "linear") != 0)
1193 major
= grub_strtoul (params
, &ptr
, 10);
1196 grub_errno
= GRUB_ERR_NONE
;
1202 minor
= grub_strtoul (ptr
, 0, 10);
1205 grub_errno
= GRUB_ERR_NONE
;
1217 convert_system_partition_to_system_disk (const char *os_dev
, struct stat
*st
)
1219 #if defined(__linux__)
1220 char *path
= xmalloc (PATH_MAX
);
1221 if (! realpath (os_dev
, path
))
1224 if (strncmp ("/dev/", path
, 5) == 0)
1228 /* If this is an IDE disk. */
1229 if (strncmp ("ide/", p
, 4) == 0)
1231 p
= strstr (p
, "part");
1238 /* If this is a SCSI disk. */
1239 if (strncmp ("scsi/", p
, 5) == 0)
1241 p
= strstr (p
, "part");
1248 /* If this is a DAC960 disk. */
1249 if (strncmp ("rd/c", p
, 4) == 0)
1251 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1252 p
= strchr (p
, 'p');
1259 /* If this is a Mylex AcceleRAID Array. */
1260 if (strncmp ("rs/c", p
, 4) == 0)
1262 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
1263 p
= strchr (p
, 'p');
1269 /* If this is a CCISS disk. */
1270 if (strncmp ("cciss/c", p
, sizeof ("cciss/c") - 1) == 0)
1272 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
1273 p
= strchr (p
, 'p');
1280 /* If this is a Compaq Intelligent Drive Array. */
1281 if (strncmp ("ida/c", p
, sizeof ("ida/c") - 1) == 0)
1283 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
1284 p
= strchr (p
, 'p');
1291 /* If this is an I2O disk. */
1292 if (strncmp ("i2o/hd", p
, sizeof ("i2o/hd") - 1) == 0)
1294 /* /dev/i2o/hd[a-z]([0-9]+)? */
1295 p
[sizeof ("i2o/hda") - 1] = '\0';
1299 /* If this is a MultiMediaCard (MMC). */
1300 if (strncmp ("mmcblk", p
, sizeof ("mmcblk") - 1) == 0)
1302 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
1303 p
= strchr (p
, 'p');
1310 if (strncmp ("md", p
, 2) == 0
1311 && p
[2] >= '0' && p
[2] <= '9')
1314 while (*ptr
>= '0' && *ptr
<= '9')
1320 /* If this is an IDE, SCSI or Virtio disk. */
1321 if (strncmp ("vdisk", p
, 5) == 0
1322 && p
[5] >= 'a' && p
[5] <= 'z')
1324 /* /dev/vdisk[a-z][0-9]* */
1328 if ((strncmp ("hd", p
, 2) == 0
1329 || strncmp ("vd", p
, 2) == 0
1330 || strncmp ("sd", p
, 2) == 0)
1331 && p
[2] >= 'a' && p
[2] <= 'z')
1334 while (*pp
>= 'a' && *pp
<= 'z')
1336 /* /dev/[hsv]d[a-z]+[0-9]* */
1341 /* If this is a Xen virtual block device. */
1342 if ((strncmp ("xvd", p
, 3) == 0) && p
[3] >= 'a' && p
[3] <= 'z')
1345 while (*pp
>= 'a' && *pp
<= 'z')
1347 /* /dev/xvd[a-z]+[0-9]* */
1352 #ifdef HAVE_DEVICE_MAPPER
1353 /* If this is a DM-RAID device.
1354 Compare os_dev rather than path here, since nodes under
1355 /dev/mapper/ are often symlinks. */
1356 if ((strncmp ("/dev/mapper/", os_dev
, 12) == 0))
1358 struct dm_tree
*tree
;
1360 struct dm_tree_node
*node
= NULL
, *child
;
1362 const char *node_uuid
, *mapper_name
= NULL
, *child_uuid
, *child_name
;
1364 tree
= dm_tree_create ();
1367 grub_dprintf ("hostdisk", "dm_tree_create failed\n");
1371 maj
= major (st
->st_rdev
);
1372 min
= minor (st
->st_rdev
);
1373 if (! dm_tree_add_dev (tree
, maj
, min
))
1375 grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
1379 node
= dm_tree_find_node (tree
, maj
, min
);
1382 grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
1385 node_uuid
= dm_tree_node_get_uuid (node
);
1388 grub_dprintf ("hostdisk", "%s has no DM uuid\n", path
);
1392 if (strncmp (node_uuid
, "LVM-", 4) == 0)
1394 grub_dprintf ("hostdisk", "%s is an LVM\n", path
);
1398 if (strncmp (node_uuid
, "mpath-", 6) == 0)
1400 /* Multipath partitions have partN-mpath-* UUIDs, and are
1401 linear mappings so are handled by
1402 grub_util_get_dm_node_linear_info. Multipath disks are not
1403 linear mappings and must be handled specially. */
1404 grub_dprintf ("hostdisk", "%s is a multipath disk\n", path
);
1405 mapper_name
= dm_tree_node_get_name (node
);
1408 if (strncmp (node_uuid
, "DMRAID-", 7) != 0)
1411 const char *node_name
;
1412 grub_dprintf ("hostdisk", "%s is not DM-RAID\n", path
);
1414 if ((node_name
= dm_tree_node_get_name (node
))
1415 && grub_util_get_dm_node_linear_info (node_name
,
1419 dm_tree_free (tree
);
1421 char *ret
= grub_find_device (NULL
, (major
<< 8) | minor
);
1430 /* Counter-intuitively, device-mapper refers to the disk-like
1431 device containing a DM-RAID partition device as a "child" of
1432 the partition device. */
1433 child
= dm_tree_next_child (&handle
, node
, 0);
1436 grub_dprintf ("hostdisk", "%s has no DM children\n", path
);
1439 child_uuid
= dm_tree_node_get_uuid (child
);
1442 grub_dprintf ("hostdisk", "%s child has no DM uuid\n", path
);
1445 else if (strncmp (child_uuid
, "DMRAID-", 7) != 0)
1447 grub_dprintf ("hostdisk", "%s child is not DM-RAID\n", path
);
1450 child_name
= dm_tree_node_get_name (child
);
1453 grub_dprintf ("hostdisk", "%s child has no DM name\n", path
);
1456 mapper_name
= child_name
;
1459 if (! mapper_name
&& node
)
1461 /* This is a DM-RAID disk, not a partition. */
1462 mapper_name
= dm_tree_node_get_name (node
);
1464 grub_dprintf ("hostdisk", "%s has no DM name\n", path
);
1467 dm_tree_free (tree
);
1470 return xasprintf ("/dev/mapper/%s", mapper_name
);
1474 #endif /* HAVE_DEVICE_MAPPER */
1479 #elif defined(__GNU__)
1480 char *path
= xstrdup (os_dev
);
1481 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
1483 char *p
= strchr (path
+ 7, 's');
1489 #elif defined(__CYGWIN__)
1490 char *path
= xstrdup (os_dev
);
1491 if (strncmp ("/dev/sd", path
, 7) == 0 && 'a' <= path
[7] && path
[7] <= 'z')
1495 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1497 if (strncmp (os_dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
1498 return xstrdup (os_dev
);
1499 follow_geom_up (os_dev
+ sizeof ("/dev/") - 1, NULL
, &out
);
1501 out2
= xasprintf ("/dev/%s", out
);
1505 #elif defined(__APPLE__)
1506 char *path
= xstrdup (os_dev
);
1507 if (strncmp ("/dev/", path
, 5) == 0)
1510 for (p
= path
+ 5; *p
; ++p
)
1511 if (grub_isdigit(*p
))
1513 p
= strpbrk (p
, "sp");
1521 #elif defined(__NetBSD__)
1522 /* NetBSD uses "/dev/r[a-z]+[0-9][a-z]". */
1523 char *path
= xstrdup (os_dev
);
1524 if (strncmp ("/dev/r", path
, sizeof("/dev/r") - 1) == 0 &&
1525 (path
[sizeof("/dev/r") - 1] >= 'a' && path
[sizeof("/dev/r") - 1] <= 'z') &&
1526 strncmp ("fd", path
+ sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
1529 for (p
= path
+ sizeof("/dev/r"); *p
>= 'a' && *p
<= 'z'; p
++);
1530 if (grub_isdigit(*p
))
1533 if ((*p
>= 'a' && *p
<= 'z') && (*(p
+1) == '\0'))
1535 /* path matches the required regular expression and
1536 p points to its last character. */
1538 # ifdef HAVE_GETRAWPARTITION
1539 rawpart
= getrawpartition();
1540 # endif /* HAVE_GETRAWPARTITION */
1549 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
1550 return xstrdup (os_dev
);
1554 #if defined(__linux__) || defined(__CYGWIN__)
1556 device_is_wholedisk (const char *os_dev
)
1558 int len
= strlen (os_dev
);
1560 if (os_dev
[len
- 1] < '0' || os_dev
[len
- 1] > '9')
1566 #if defined(__NetBSD__)
1567 /* Try to determine whether a given device name corresponds to a whole disk.
1568 This function should give in most cases a definite answer, but it may
1569 actually give an approximate one in the following sense: if the return
1570 value is 0 then the device name does not correspond to a whole disk. */
1572 device_is_wholedisk (const char *os_dev
)
1574 int len
= strlen (os_dev
);
1577 # ifdef HAVE_GETRAWPARTITION
1578 rawpart
= getrawpartition();
1579 # endif /* HAVE_GETRAWPARTITION */
1582 return (os_dev
[len
- 1] == ('a' + rawpart
));
1584 #endif /* defined(__NetBSD__) */
1586 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1588 device_is_wholedisk (const char *os_dev
)
1592 if (strncmp (os_dev
, "/dev/", sizeof ("/dev/") - 1) != 0)
1595 for (p
= os_dev
+ sizeof ("/dev/") - 1; *p
; ++p
)
1596 if (grub_isdigit (*p
))
1598 if (strchr (p
, 's'))
1605 #endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
1608 find_system_device (const char *os_dev
, struct stat
*st
, int convert
, int add
)
1614 os_disk
= convert_system_partition_to_system_disk (os_dev
, st
);
1616 os_disk
= xstrdup (os_dev
);
1620 for (i
= 0; i
< ARRAY_SIZE (map
); i
++)
1621 if (! map
[i
].device
)
1623 else if (strcmp (map
[i
].device
, os_disk
) == 0)
1632 if (i
== ARRAY_SIZE (map
))
1633 grub_util_error (_("device count exceeds limit"));
1635 map
[i
].device
= os_disk
;
1636 map
[i
].drive
= xmalloc (sizeof ("hostdisk/") + strlen (os_disk
));
1637 strcpy (map
[i
].drive
, "hostdisk/");
1638 strcpy (map
[i
].drive
+ sizeof ("hostdisk/") - 1, os_disk
);
1639 map
[i
].device_map
= 0;
1645 grub_util_biosdisk_is_present (const char *os_dev
)
1649 if (stat (os_dev
, &st
) < 0)
1652 return find_system_device (os_dev
, &st
, 1, 0) != -1;
1656 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
1661 if (stat (os_dev
, &st
) < 0)
1663 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
1664 grub_util_info ("cannot stat `%s'", os_dev
);
1668 drive
= find_system_device (os_dev
, &st
, 1, 1);
1671 grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
1672 "no mapping exists for `%s'", os_dev
);
1673 grub_util_info ("no mapping exists for `%s'", os_dev
);
1677 if (grub_strcmp (os_dev
,
1678 convert_system_partition_to_system_disk (os_dev
, &st
)) == 0)
1679 return make_device_name (drive
, -1, -1);
1681 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
1682 if (! S_ISCHR (st
.st_mode
))
1684 if (! S_ISBLK (st
.st_mode
))
1686 return make_device_name (drive
, -1, -1);
1688 #if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1690 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
1691 partition, so mapping them to GRUB devices is not trivial.
1692 Here, get the start sector of a partition by HDIO_GETGEO, and
1693 compare it with each partition GRUB recognizes.
1695 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
1696 does not count the extended partition and missing primary
1697 partitions. Use same method as on Linux here.
1699 For NetBSD and FreeBSD, proceed as for Linux, except that the start
1700 sector is obtained from the disk label. */
1702 char *name
, *partname
;
1704 grub_disk_addr_t start
;
1705 auto int find_partition (grub_disk_t dsk
,
1706 const grub_partition_t partition
);
1708 int find_partition (grub_disk_t dsk
__attribute__ ((unused
)),
1709 const grub_partition_t partition
)
1711 grub_disk_addr_t part_start
= 0;
1712 grub_util_info ("Partition %d starts from %lu",
1713 partition
->number
, partition
->start
);
1715 part_start
= grub_partition_get_start (partition
);
1717 if (start
== part_start
)
1719 partname
= grub_partition_get_name (partition
);
1726 name
= make_device_name (drive
, -1, -1);
1728 # if !defined(HAVE_DIOCGDINFO)
1729 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
1731 # else /* defined(HAVE_DIOCGDINFO) */
1732 /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
1733 * different, we know that os_dev cannot be a floppy device. */
1734 # endif /* !defined(HAVE_DIOCGDINFO) */
1736 start
= find_partition_start (os_dev
);
1737 if (grub_errno
!= GRUB_ERR_NONE
)
1743 grub_util_info ("%s starts from %lu", os_dev
, start
);
1745 if (start
== 0 && device_is_wholedisk (os_dev
))
1748 grub_util_info ("opening the device %s", name
);
1749 disk
= grub_disk_open (name
);
1754 /* We already know that the partition exists. Given that we already
1755 checked the device map above, we can only get
1756 GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
1757 This can happen on Xen, where disk images in the host can be
1758 assigned to devices that have partition-like names in the guest
1759 but are really more like disks. */
1760 if (grub_errno
== GRUB_ERR_UNKNOWN_DEVICE
)
1763 ("disk does not exist, so falling back to partition device %s",
1766 drive
= find_system_device (os_dev
, &st
, 0, 1);
1769 grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
1770 "no mapping exists for `%s'", os_dev
);
1774 return make_device_name (drive
, -1, -1);
1781 grub_partition_iterate (disk
, find_partition
);
1782 if (grub_errno
!= GRUB_ERR_NONE
)
1784 grub_disk_close (disk
);
1788 if (partname
== NULL
)
1790 grub_disk_close (disk
);
1791 grub_error (GRUB_ERR_BAD_DEVICE
,
1792 "cannot find the partition of `%s'", os_dev
);
1796 name
= grub_xasprintf ("%s,%s", disk
->name
, partname
);
1801 #elif defined(__GNU__)
1802 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
1808 p
= strrchr (os_dev
, 's');
1815 n
= strtol (p
, &q
, 10);
1816 if (p
!= q
&& n
!= GRUB_LONG_MIN
&& n
!= GRUB_LONG_MAX
)
1818 dos_part
= (int) n
- 1;
1820 if (*q
>= 'a' && *q
<= 'g')
1821 bsd_part
= *q
- 'a';
1825 return make_device_name (drive
, dos_part
, bsd_part
);
1829 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
1830 return make_device_name (drive
, -1, -1);
1835 grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk
)
1837 if (disk
->dev
!= &grub_util_biosdisk_dev
|| map
[disk
->id
].device_map
)
1843 grub_util_biosdisk_get_osdev (grub_disk_t disk
)
1845 return map
[disk
->id
].device
;
1849 grub_util_biosdisk_is_floppy (grub_disk_t disk
)
1854 fd
= open (map
[disk
->id
].device
, O_RDONLY
);
1855 /* Shouldn't happen. */
1859 /* Shouldn't happen either. */
1860 if (fstat (fd
, &st
) < 0)
1863 #if defined(__NetBSD__)
1864 if (major(st
.st_rdev
) == RAW_FLOPPY_MAJOR
)
1868 #if defined(FLOPPY_MAJOR)
1869 if (major(st
.st_rdev
) == FLOPPY_MAJOR
)
1871 /* Some kernels (e.g. kFreeBSD) don't have a static major number
1872 for floppies, but they still use a "fd[0-9]" pathname. */
1873 if (map
[disk
->id
].device
[5] == 'f'
1874 && map
[disk
->id
].device
[6] == 'd'
1875 && map
[disk
->id
].device
[7] >= '0'
1876 && map
[disk
->id
].device
[7] <= '9')