]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
* grub-core/kern/emu/hostdisk.c (open_device): New argument max. All
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 18 Apr 2012 21:18:51 +0000 (23:18 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 18 Apr 2012 21:18:51 +0000 (23:18 +0200)
users updated.
(grub_util_biosdisk_read): Handle Linux partitions not exactly
corresponding to GRUB partitions.
(grub_util_biosdisk_write): Likewise.

ChangeLog
grub-core/kern/emu/hostdisk.c

index e9e0edde67523f37a91c17bca7aa00ff4db24dea..88921d7b66ce63e31ca9a21e0071db1acc548ed6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index 895edf14934a664df9b49509e6c20300f420fcaf..be3a6653f103d2be5ba437b9a95ed3fcd8f39314 100644 (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)
-      is_partition = linux_find_partition (dev, part_start);
+      {
+       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)
-      sector -= part_start;
+      {
+       *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,90 +1033,87 @@ 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));
-    }
+      int fd;
+      grub_disk_addr_t max = ~0ULL;
+      fd = open_device (disk, sector, O_RDONLY, &max);
+      if (fd < 0)
+       return grub_errno;
 
-  fd = open_device (disk, sector, O_RDONLY);
-  if (fd < 0)
-    return grub_errno;
+      if (max > size)
+       max = size;
 
 #ifdef __linux__
-  if (sector == 0 && size > 1)
-    {
-      /* Work around a bug in Linux ez remapping.  Linux remaps all
-        sectors that are read together with the MBR in one read.  It
-        should only remap the MBR, so we split the read in two
-        parts. -jochen  */
-      if (grub_util_fd_read (fd, buf, (1 << disk->log_sector_size))
-         != (1 << disk->log_sector_size))
+      if (sector == 0 && max > 1)
        {
-         grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
-                     map[disk->id].device, strerror (errno));
-         return grub_errno;
+         /* Work around a bug in Linux ez remapping.  Linux remaps all
+            sectors that are read together with the MBR in one read.  It
+            should only remap the MBR, so we split the read in two
+            parts. -jochen  */
+         if (grub_util_fd_read (fd, buf, (1 << disk->log_sector_size))
+             != (1 << disk->log_sector_size))
+           return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"),
+                              map[disk->id].device, strerror (errno));
+         
+         buf += (1 << disk->log_sector_size);
+         size--;
+         max--;
        }
-
-      buf += (1 << disk->log_sector_size);
-      size--;
-    }
 #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"),
-               map[disk->id].device, strerror (errno));
-
-  return grub_errno;
+      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));
+      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));
-    }
+      int fd;
+      grub_disk_addr_t max = ~0ULL;
+      fd = open_device (disk, sector, O_RDONLY, &max);
+      if (fd < 0)
+       return grub_errno;
 
-  fd = open_device (disk, sector, O_WRONLY);
-  if (fd < 0)
-    return grub_errno;
+      if (max > size)
+       max = size;
 
-  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"),
-               map[disk->id].device, strerror (errno));
+#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));
+         
+         buf += (1 << disk->log_sector_size);
+         size--;
+         max--;
+       }
+#endif /* __linux__ */
 
-  return grub_errno;
+      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;
     }