* grub-core/kern/emu/hostdisk.c (open_device): New argument max. All

users updated.
	(grub_util_biosdisk_read): Handle Linux partitions not exactly
	corresponding to GRUB partitions.
	(grub_util_biosdisk_write): Likewise.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2012-04-18 23:18:51 +02:00
parent b72d44a11a
commit cb7f944e44
2 changed files with 102 additions and 72 deletions

View file

@ -1,3 +1,11 @@
2012-04-18 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/kern/emu/hostdisk.c (open_device): New argument max. All
users updated.
(grub_util_biosdisk_read): Handle Linux partitions not exactly
corresponding to GRUB partitions.
(grub_util_biosdisk_write): Likewise.
2012-04-18 Vladimir Serbinenko <phcoder@gmail.com>
Scan mdraid before LVM.

View file

@ -790,11 +790,14 @@ grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add)
}
static int
open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags,
grub_disk_addr_t *max)
{
int fd;
struct grub_util_biosdisk_data *data = disk->data;
*max = ~0ULL;
#ifdef O_LARGEFILE
flags |= O_LARGEFILE;
#endif
@ -819,9 +822,16 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
part_start = grub_partition_get_start (disk->partition);
strcpy (dev, map[disk->id].device);
if (disk->partition && sector >= part_start
if (disk->partition
&& strncmp (map[disk->id].device, "/dev/", 5) == 0)
{
if (sector >= part_start)
is_partition = linux_find_partition (dev, part_start);
else
*max = part_start - sector;
}
reopen:
if (data->dev && strcmp (data->dev, dev) == 0 &&
data->access_mode == (flags & O_ACCMODE))
@ -863,7 +873,21 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
}
if (is_partition)
{
*max = grub_util_get_fd_size (fd, dev, 0);
*max >>= disk->log_sector_size;
if (sector - part_start >= *max)
{
*max = disk->partition->len - (sector - part_start);
if (*max == 0)
*max = ~0ULL;
is_partition = 0;
strcpy (dev, map[disk->id].device);
goto reopen;
}
sector -= part_start;
*max -= sector;
}
}
#else /* ! __linux__ */
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
@ -1009,31 +1033,19 @@ static grub_err_t
grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, char *buf)
{
int fd;
/* Split pre-partition and partition reads. */
if (disk->partition && sector < disk->partition->start
&& sector + size > disk->partition->start)
while (size)
{
grub_err_t err;
err = grub_util_biosdisk_read (disk, sector,
disk->partition->start - sector,
buf);
if (err)
return err;
return grub_util_biosdisk_read (disk, disk->partition->start,
size - (disk->partition->start - sector),
buf + ((disk->partition->start - sector)
<< GRUB_DISK_SECTOR_BITS));
}
fd = open_device (disk, sector, O_RDONLY);
int fd;
grub_disk_addr_t max = ~0ULL;
fd = open_device (disk, sector, O_RDONLY, &max);
if (fd < 0)
return grub_errno;
if (max > size)
max = size;
#ifdef __linux__
if (sector == 0 && size > 1)
if (sector == 0 && max > 1)
{
/* Work around a bug in Linux ez remapping. Linux remaps all
sectors that are read together with the MBR in one read. It
@ -1041,58 +1053,67 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
parts. -jochen */
if (grub_util_fd_read (fd, buf, (1 << disk->log_sector_size))
!= (1 << disk->log_sector_size))
{
grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
map[disk->id].device, strerror (errno));
return grub_errno;
}
buf += (1 << disk->log_sector_size);
size--;
max--;
}
#endif /* __linux__ */
if (grub_util_fd_read (fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << disk->log_sector_size))
grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
if (grub_util_fd_read (fd, buf, max << disk->log_sector_size)
!= (ssize_t) (max << disk->log_sector_size))
return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
map[disk->id].device, strerror (errno));
return grub_errno;
size -= max;
buf += (max << disk->log_sector_size);
sector += max;
}
return GRUB_ERR_NONE;
}
static grub_err_t
grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
grub_size_t size, const char *buf)
{
int fd;
/* Split pre-partition and partition writes. */
if (disk->partition && sector < disk->partition->start
&& sector + size > disk->partition->start)
while (size)
{
grub_err_t err;
err = grub_util_biosdisk_write (disk, sector,
disk->partition->start - sector,
buf);
if (err)
return err;
return grub_util_biosdisk_write (disk, disk->partition->start,
size - (disk->partition->start - sector),
buf + ((disk->partition->start - sector)
<< GRUB_DISK_SECTOR_BITS));
}
fd = open_device (disk, sector, O_WRONLY);
int fd;
grub_disk_addr_t max = ~0ULL;
fd = open_device (disk, sector, O_RDONLY, &max);
if (fd < 0)
return grub_errno;
if (grub_util_fd_write (fd, buf, size << disk->log_sector_size)
!= (ssize_t) (size << disk->log_sector_size))
grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write to `%s': %s"),
if (max > size)
max = size;
#ifdef __linux__
if (sector == 0 && max > 1)
{
/* Work around a bug in Linux ez remapping. Linux remaps all
sectors that are write together with the MBR in one write. It
should only remap the MBR, so we split the write in two
parts. -jochen */
if (grub_util_fd_write (fd, buf, (1 << disk->log_sector_size))
!= (1 << disk->log_sector_size))
return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write `%s': %s"),
map[disk->id].device, strerror (errno));
return grub_errno;
buf += (1 << disk->log_sector_size);
size--;
max--;
}
#endif /* __linux__ */
if (grub_util_fd_write (fd, buf, max << disk->log_sector_size)
!= (ssize_t) (max << disk->log_sector_size))
return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write `%s': %s"),
map[disk->id].device, strerror (errno));
size -= max;
buf += (max << disk->log_sector_size);
}
return GRUB_ERR_NONE;
}
grub_err_t
@ -1104,7 +1125,8 @@ grub_util_biosdisk_flush (struct grub_disk *disk)
return GRUB_ERR_NONE;
if (data->fd == -1)
{
data->fd = open_device (disk, 0, O_RDONLY);
grub_disk_addr_t max;
data->fd = open_device (disk, 0, O_RDONLY, &max);
if (data->fd < 0)
return grub_errno;
}