]> git.proxmox.com Git - grub2.git/blobdiff - grub-core/kern/emu/hostdisk.c
merge mainline into hints
[grub2.git] / grub-core / kern / emu / hostdisk.c
index 8f30c8a09e8b2348c48d162020ac70bea9678c0f..d9cf07cf4b211825c76b2834307f0c8e83c85cc0 100644 (file)
@@ -43,6 +43,7 @@
 
 #ifdef __linux__
 # include <sys/ioctl.h>         /* ioctl */
+# include <sys/mount.h>
 # if !defined(__GLIBC__) || \
         ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
 /* Maybe libc doesn't have large file support.  */
@@ -93,10 +94,16 @@ struct hd_geometry
 # include <sys/disk.h> /* DIOCGMEDIASIZE */
 # include <sys/param.h>
 # include <sys/sysctl.h>
+# include <sys/mount.h>
+#include <libgeom.h>
 # define MAJOR(dev) major(dev)
 # define FLOPPY_MAJOR  2
 #endif
 
+#if defined (__sun__)
+# include <sys/dkio.h>
+#endif
+
 #if defined(__APPLE__)
 # include <sys/disk.h>
 #endif
@@ -105,9 +112,7 @@ struct hd_geometry
 # include <libdevmapper.h>
 #endif
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <libgeom.h>
-#elif defined(__NetBSD__)
+#if defined(__NetBSD__)
 # define HAVE_DIOCGDINFO
 # include <sys/ioctl.h>
 # include <sys/disklabel.h>    /* struct disklabel */
@@ -187,6 +192,27 @@ configure_device_driver (int fd)
 }
 #endif /* defined(__NetBSD__) */
 
+static int
+unescape_cmp (const char *a, const char *b_escaped)
+{
+  while (*a || *b_escaped)
+    {
+      if (*b_escaped == '\\' && b_escaped[1] != 0)
+       b_escaped++;
+      if (*a < *b_escaped)
+       return -1;
+      if (*a > *b_escaped)
+       return +1;
+      a++;
+      b_escaped++;
+    }
+  if (*a)
+    return +1;
+  if (*b_escaped)
+    return -1;
+  return 0;
+}
+
 static int
 find_grub_drive (const char *name)
 {
@@ -195,7 +221,7 @@ find_grub_drive (const char *name)
   if (name)
     {
       for (i = 0; i < ARRAY_SIZE (map); i++)
-       if (map[i].drive && ! strcmp (map[i].drive, name))
+       if (map[i].drive && unescape_cmp (map[i].drive, name) == 0)
          return i;
     }
 
@@ -215,10 +241,14 @@ find_free_slot (void)
 }
 
 static int
-grub_util_biosdisk_iterate (int (*hook) (const char *name))
+grub_util_biosdisk_iterate (int (*hook) (const char *name),
+                           grub_disk_pull_t pull)
 {
   unsigned i;
 
+  if (pull != GRUB_DISK_PULL_NONE)
+    return 0;
+
   for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
     if (map[i].drive && hook (map[i].drive))
       return 1;
@@ -226,6 +256,98 @@ grub_util_biosdisk_iterate (int (*hook) (const char *name))
   return 0;
 }
 
+#if !defined(__MINGW32__)
+grub_uint64_t
+grub_util_get_fd_sectors (int fd, unsigned *log_secsize)
+{
+# if defined(__NetBSD__)
+  struct disklabel label;
+# elif defined (__sun__)
+  struct dk_minfo minfo;
+#else
+  unsigned long long nr;
+# endif
+  unsigned sector_size, log_sector_size;
+  struct stat st;
+
+  if (fstat (fd, &st) < 0)
+    grub_util_error (_("fstat failed"));
+
+#if defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
+  defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) \
+  || defined (__sun__)
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)
+  if (! S_ISCHR (st.st_mode))
+# else
+  if (! S_ISBLK (st.st_mode))
+# endif
+    goto fail;
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    if (ioctl (fd, DIOCGMEDIASIZE, &nr))
+# elif defined(__APPLE__)
+    if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
+# elif defined(__NetBSD__)
+    configure_device_driver (fd);
+    if (ioctl (fd, DIOCGDINFO, &label) == -1)
+# elif defined (__sun__)
+    if (!ioctl (fd, DKIOCGMEDIAINFO, &minfo))
+# else
+    if (ioctl (fd, BLKGETSIZE64, &nr))
+# endif
+      goto fail;
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+    if (ioctl (fd, DIOCGSECTORSIZE, &sector_size))
+      goto fail;
+# elif defined(__sun__)
+    sector_size = minfo.dki_lbsize;
+# elif defined(__NetBSD__)
+    sector_size = label.d_secsize;
+# else
+    if (ioctl (fd, BLKSSZGET, &sector_size))
+      goto fail;
+# endif
+    if (sector_size & (sector_size - 1) || !sector_size)
+      goto fail;
+    for (log_sector_size = 0;
+        (1 << log_sector_size) < sector_size;
+        log_sector_size++);
+
+    if (log_secsize)
+      *log_secsize = log_sector_size;
+
+# if defined (__APPLE__)
+    return nr;
+# elif defined(__NetBSD__)
+    return label.d_secperunit;
+# elif defined (__sun__)
+    return minfo.dki_capacity;
+# else
+    if (nr & ((1 << log_sector_size) - 1))
+      grub_util_error (_("unaligned device size"));
+
+    return (nr >> log_sector_size);
+# endif
+
+ fail:
+
+  /* In GNU/Hurd, stat() will return the right size.  */
+#elif !defined (__GNU__)
+# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
+#endif
+
+  sector_size = 512;
+  log_sector_size = 9;
+
+  if (log_secsize)
+   *log_secsize = 9;
+
+  return st.st_size >> 9;
+}
+#endif
+
 static grub_err_t
 grub_util_biosdisk_open (const char *name, grub_disk_t disk)
 {
@@ -254,7 +376,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
     size = grub_util_get_disk_size (map[drive].device);
 
     if (size % 512)
-      grub_util_error ("unaligned device size");
+      grub_util_error (_("unaligned device size"));
 
     disk->total_sectors = size >> 9;
 
@@ -262,77 +384,30 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
 
     return GRUB_ERR_NONE;
   }
-#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
-      defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+#else
   {
-# if defined(__NetBSD__)
-    struct disklabel label;
-# else
-    unsigned long long nr;
-# endif
     int fd;
 
     fd = open (map[drive].device, O_RDONLY);
     if (fd == -1)
       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
 
+    disk->total_sectors = grub_util_get_fd_sectors (fd, &disk->log_sector_size);
+
 # if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
     if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
 # else
     if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
 # endif
-      {
-       close (fd);
-       goto fail;
-      }
-    data->is_disk = 1;
-
-# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    if (ioctl (fd, DIOCGMEDIASIZE, &nr))
-# elif defined(__APPLE__)
-    if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
-# elif defined(__NetBSD__)
-    configure_device_driver (fd);
-    if (ioctl (fd, DIOCGDINFO, &label) == -1)
-# else
-    if (ioctl (fd, BLKGETSIZE64, &nr))
-# endif
-      {
-       close (fd);
-       goto fail;
-      }
+      data->is_disk = 1;
 
     close (fd);
 
-# if defined (__APPLE__)
-    disk->total_sectors = nr;
-# elif defined(__NetBSD__)
-    disk->total_sectors = label.d_secperunit;
-# else
-    disk->total_sectors = nr / 512;
-
-    if (nr % 512)
-      grub_util_error ("unaligned device size");
-# endif
-
     grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
 
     return GRUB_ERR_NONE;
   }
-
- fail:
-  /* In GNU/Hurd, stat() will return the right size.  */
-#elif !defined (__GNU__)
-# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
 #endif
-  if (stat (map[drive].device, &st) < 0)
-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device);
-
-  disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
-
-  grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
-
-  return GRUB_ERR_NONE;
 }
 
 int
@@ -353,11 +428,13 @@ grub_util_device_is_mapped (const char *dev)
 #endif /* HAVE_DEVICE_MAPPER */
 }
 
+
 #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+
 /* FIXME: geom actually gives us the whole container hierarchy.
    It can be used more efficiently than this.  */
-static void
-follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
+void
+grub_util_follow_gpart_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
 {
   struct gmesh mesh;
   struct gclass *class;
@@ -368,13 +445,13 @@ follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
 
   error = geom_gettree (&mesh);
   if (error != 0)
-    grub_util_error ("couldn't open geom");
+    grub_util_error (_("couldn't open geom"));
 
   LIST_FOREACH (class, &mesh.lg_class, lg_class)
     if (strcasecmp (class->lg_name, "part") == 0)
       break;
   if (!class)
-    grub_util_error ("couldn't open geom part");
+    grub_util_error (_("couldn't open geom part"));
 
   LIST_FOREACH (geom, &class->lg_geom, lg_geom)
     { 
@@ -387,7 +464,7 @@ follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
            struct gconfig *config;
            grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name);
 
-           follow_geom_up (name_tmp, &off, name_out);
+           grub_util_follow_gpart_up (name_tmp, &off, name_out);
            free (name_tmp);
            LIST_FOREACH (config, &provider->lg_config, lg_config)
              if (strcasecmp (config->lg_name, "start") == 0)
@@ -410,16 +487,19 @@ find_partition_start (const char *dev)
   grub_disk_addr_t out;
   if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
     return 0;
-  follow_geom_up (dev + sizeof ("/dev/") - 1, &out, NULL);
+  grub_util_follow_gpart_up (dev + sizeof ("/dev/") - 1, &out, NULL);
 
   return out;
 }
-#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO)
+
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined (__sun__)
 static grub_disk_addr_t
 find_partition_start (const char *dev)
 {
   int fd;
-# if !defined(HAVE_DIOCGDINFO)
+#ifdef __sun__
+  struct extpart_info pinfo;
+# elif !defined(HAVE_DIOCGDINFO)
   struct hd_geometry hdg;
 # else /* defined(HAVE_DIOCGDINFO) */
   struct disklabel label;
@@ -506,7 +586,9 @@ devmapper_fail:
       return 0;
     }
 
-# if !defined(HAVE_DIOCGDINFO)
+#if defined(__sun__)
+  if (ioctl (fd, DKIOCEXTPARTINFO, &pinfo))
+# elif !defined(HAVE_DIOCGDINFO)
   if (ioctl (fd, HDIO_GETGEO, &hdg))
 # else /* defined(HAVE_DIOCGDINFO) */
 #  if defined(__NetBSD__)
@@ -527,7 +609,9 @@ devmapper_fail:
 
   close (fd);
 
-# if !defined(HAVE_DIOCGDINFO)
+#ifdef __sun__
+  return pinfo.p_start;
+# elif !defined(HAVE_DIOCGDINFO)
   return hdg.start;
 # else /* defined(HAVE_DIOCGDINFO) */
   if (dev[0])
@@ -567,6 +651,7 @@ linux_find_partition (char *dev, grub_disk_addr_t sector)
   int i;
   char real_dev[PATH_MAX];
   struct linux_partition_cache *cache;
+  int missing = 0;
 
   strcpy(real_dev, dev);
 
@@ -575,6 +660,12 @@ linux_find_partition (char *dev, grub_disk_addr_t sector)
       p = real_dev + len - 4;
       format = "part%d";
     }
+  else if (strncmp (real_dev, "/dev/disk/by-id/",
+                   sizeof ("/dev/disk/by-id/") - 1) == 0)
+    {
+      p = real_dev + len;
+      format = "-part%d";
+    }
   else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
     {
       p = real_dev + len;
@@ -605,7 +696,13 @@ linux_find_partition (char *dev, grub_disk_addr_t sector)
 
       fd = open (real_dev, O_RDONLY);
       if (fd == -1)
-       continue;
+       {
+         if (missing++ < 10)
+           continue;
+         else
+           return 0;
+       }
+      missing = 0;
       close (fd);
 
       start = find_partition_start (real_dev);
@@ -632,6 +729,37 @@ linux_find_partition (char *dev, grub_disk_addr_t sector)
 }
 #endif /* __linux__ */
 
+#if defined(__linux__) && (!defined(__GLIBC__) || \
+        ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
+  /* Maybe libc doesn't have large file support.  */
+grub_err_t
+grub_util_fd_seek (int fd, const char *name, grub_uint64_t off)
+{
+  loff_t offset, result;
+  static int _llseek (uint filedes, ulong hi, ulong lo,
+                     loff_t *res, uint wh);
+  _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
+            loff_t *, res, uint, wh);
+
+  offset = (loff_t) off;
+  if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
+    {
+      return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name);
+    }
+  return GRUB_ERR_NONE;
+}
+#else
+grub_err_t
+grub_util_fd_seek (int fd, const char *name, grub_uint64_t off)
+{
+  off_t offset = (off_t) off;
+
+  if (lseek (fd, offset, SEEK_SET) != offset)
+    return grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", name);
+  return 0;
+}
+#endif
+
 static int
 open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
 {
@@ -784,44 +912,20 @@ open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
   configure_device_driver (fd);
 #endif /* defined(__NetBSD__) */
 
-#if defined(__linux__) && (!defined(__GLIBC__) || \
-        ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
-  /* Maybe libc doesn't have large file support.  */
-  {
-    loff_t offset, result;
-    static int _llseek (uint filedes, ulong hi, ulong lo,
-                        loff_t *res, uint wh);
-    _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
-               loff_t *, res, uint, wh);
-
-    offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
-    if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
-      {
-       grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
-       close (fd);
-       return -1;
-      }
-  }
-#else
-  {
-    off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
-
-    if (lseek (fd, offset, SEEK_SET) != offset)
-      {
-       grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
-       close (fd);
-       return -1;
-      }
-  }
-#endif
+  if (grub_util_fd_seek (fd, map[disk->id].device,
+                        sector << disk->log_sector_size))
+    {
+      close (fd);
+      return -1;
+    }
 
   return fd;
 }
 
 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
    error occurs, otherwise return LEN.  */
-static ssize_t
-nread (int fd, char *buf, size_t len)
+ssize_t
+grub_util_fd_read (int fd, char *buf, size_t len)
 {
   ssize_t size = len;
 
@@ -904,19 +1008,20 @@ grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
         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 (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
+      if (grub_util_fd_read (fd, buf, (1 << disk->log_sector_size))
+         != (1 << disk->log_sector_size))
        {
          grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
          return grub_errno;
        }
 
-      buf += GRUB_DISK_SECTOR_SIZE;
+      buf += (1 << disk->log_sector_size);
       size--;
     }
 #endif /* __linux__ */
 
-  if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
-      != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+  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, "cannot read from `%s'", map[disk->id].device);
 
   return grub_errno;
@@ -949,8 +1054,8 @@ grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
   if (fd < 0)
     return grub_errno;
 
-  if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
-      != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+  if (nwrite (fd, buf, size << disk->log_sector_size)
+      != (ssize_t) (size << disk->log_sector_size))
     grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
 
   return grub_errno;
@@ -1048,18 +1153,18 @@ read_device_map (const char *dev_map)
        continue;
 
       if (*p != '(')
-       show_error ("No open parenthesis found");
+       show_error (_("No open parenthesis found"));
 
       p++;
       /* Find a free slot.  */
       drive = find_free_slot ();
       if (drive < 0)
-       show_error ("Map table size exceeded");
+       show_error (_("Map table size exceeded"));
 
       e = p;
       p = strchr (p, ')');
       if (! p)
-       show_error ("No close parenthesis found");
+       show_error (_("No close parenthesis found"));
 
       map[drive].drive = xmalloc (p - e + sizeof ('\0'));
       strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
@@ -1072,7 +1177,7 @@ read_device_map (const char *dev_map)
        p++;
 
       if (*p == '\0')
-       show_error ("No filename found");
+       show_error (_("No filename found"));
 
       /* NUL-terminate the filename.  */
       e = p;
@@ -1101,7 +1206,7 @@ read_device_map (const char *dev_map)
        {
          map[drive].device = xmalloc (PATH_MAX);
          if (! realpath (p, map[drive].device))
-           grub_util_error ("cannot get the real path of `%s'", p);
+           grub_util_error (_("cannot get the real path of `%s'"), p);
        }
       else
 #endif
@@ -1142,25 +1247,28 @@ grub_util_biosdisk_fini (void)
 static char *
 make_device_name (int drive, int dos_part, int bsd_part)
 {
-  char *ret;
-  char *dos_part_str = NULL;
-  char *bsd_part_str = NULL;
-
+  char *ret, *ptr, *end;
+  const char *iptr;
+
+  ret = xmalloc (strlen (map[drive].drive) * 2 
+                + sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
+                          ",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
+  end = (ret + strlen (map[drive].drive) * 2 
+        + sizeof (",XXXXXXXXXXXXXXXXXXXXXXXXXX"
+                  ",XXXXXXXXXXXXXXXXXXXXXXXXXX"));
+  ptr = ret;
+  for (iptr = map[drive].drive; *iptr; iptr++)
+    {
+      if (*iptr == ',')
+       *ptr++ = '\\';
+      *ptr++ = *iptr;
+    }
+  *ptr = 0;
   if (dos_part >= 0)
-    dos_part_str = xasprintf (",%d", dos_part + 1);
-
+    snprintf (ptr, end - ptr, ",%d", dos_part + 1);
+  ptr += strlen (ptr);
   if (bsd_part >= 0)
-    bsd_part_str = xasprintf (",%d", bsd_part + 1);
-
-  ret = xasprintf ("%s%s%s", map[drive].drive,
-                   dos_part_str ? : "",
-                   bsd_part_str ? : "");
-
-  if (dos_part_str)
-    free (dos_part_str);
-
-  if (bsd_part_str)
-    free (bsd_part_str);
+    snprintf (ptr, end - ptr, ",%d", bsd_part + 1); 
 
   return ret;
 }
@@ -1418,7 +1526,8 @@ convert_system_partition_to_system_disk (const char *os_dev, struct stat *st)
                  if (tree)
                    dm_tree_free (tree);
                  free (path);
-                 char *ret = grub_find_device (NULL, (major << 8) | minor);
+                 char *ret = grub_find_device ("/dev",
+                                               (major << 8) | minor);
                  return ret;
                }
 
@@ -1496,7 +1605,7 @@ devmapper_out:
   char *out, *out2;
   if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
     return xstrdup (os_dev);
-  follow_geom_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
+  grub_util_follow_gpart_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
 
   out2 = xasprintf ("/dev/%s", out);
   free (out);
@@ -1545,12 +1654,37 @@ devmapper_out:
     }
   return path;
 
+#elif defined (__sun__)
+  char *colon = grub_strrchr (os_dev, ':');
+  if (grub_memcmp (os_dev, "/devices", sizeof ("/devices") - 1) == 0
+      && colon)
+    {
+      char *ret = xmalloc (colon - os_dev + sizeof (":q,raw"));
+      grub_memcpy (ret, os_dev, colon - os_dev);
+      grub_memcpy (ret + (colon - os_dev), ":q,raw", sizeof (":q,raw"));
+      return ret;
+    }
+  else
+    return xstrdup (os_dev);
 #else
 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
   return xstrdup (os_dev);
 #endif
 }
 
+#if defined(__sun__)
+static int
+device_is_wholedisk (const char *os_dev)
+{
+  if (grub_memcmp (os_dev, "/devices/", sizeof ("/devices/") - 1) != 0)
+    return 1;
+  if (grub_memcmp (os_dev + strlen (os_dev) - (sizeof (":q,raw") - 1),
+                  ":q,raw", (sizeof (":q,raw") - 1)) == 0)
+    return 1;
+  return 0;
+}
+#endif
+
 #if defined(__linux__) || defined(__CYGWIN__)
 static int
 device_is_wholedisk (const char *os_dev)
@@ -1627,7 +1761,10 @@ find_system_device (const char *os_dev, struct stat *st, int convert, int add)
       }
 
   if (!add)
-    return -1;
+    {
+      free (os_disk);
+      return -1;
+    }
 
   if (i == ARRAY_SIZE (map))
     grub_util_error (_("device count exceeds limit"));
@@ -1657,6 +1794,9 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
 {
   struct stat st;
   int drive;
+  char *sys_disk;
+
+  grub_util_info ("Looking for %s", os_dev);
 
   if (stat (os_dev, &st) < 0)
     {
@@ -1674,18 +1814,22 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
       return 0;
     }
 
-  if (grub_strcmp (os_dev,
-                  convert_system_partition_to_system_disk (os_dev, &st)) == 0)
-    return make_device_name (drive, -1, -1);
+  sys_disk = convert_system_partition_to_system_disk (os_dev, &st);
+  if (grub_strcmp (os_dev, sys_disk) == 0)
+    {
+      free (sys_disk);
+      return make_device_name (drive, -1, -1);
+    }
+  free (sys_disk);
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__) || defined (__sun__)
   if (! S_ISCHR (st.st_mode))
 #else
   if (! S_ISBLK (st.st_mode))
 #endif
     return make_device_name (drive, -1, -1);
 
-#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__sun__)
 
   /* Linux counts partitions uniformly, whether a BSD partition or a DOS
      partition, so mapping them to GRUB devices is not trivial.
@@ -1725,7 +1869,7 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
 
     name = make_device_name (drive, -1, -1);
 
-# if !defined(HAVE_DIOCGDINFO)
+# if !defined(HAVE_DIOCGDINFO) && !defined(__sun__)
     if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
       return name;
 # else /* defined(HAVE_DIOCGDINFO) */
@@ -1788,6 +1932,7 @@ grub_util_biosdisk_get_grub_dev (const char *os_dev)
     if (partname == NULL)
       {
        grub_disk_close (disk);
+       grub_util_info ("cannot find the partition of `%s'", os_dev);
        grub_error (GRUB_ERR_BAD_DEVICE,
                    "cannot find the partition of `%s'", os_dev);
        return 0;
@@ -1851,6 +1996,9 @@ grub_util_biosdisk_is_floppy (grub_disk_t disk)
   struct stat st;
   int fd;
 
+  if (disk->dev != &grub_util_biosdisk_dev)
+    return 0;
+
   fd = open (map[disk->id].device, O_RDONLY);
   /* Shouldn't happen.  */
   if (fd == -1)
@@ -1858,7 +2006,12 @@ grub_util_biosdisk_is_floppy (grub_disk_t disk)
 
   /* Shouldn't happen either.  */
   if (fstat (fd, &st) < 0)
-    return 0;
+    {
+      close (fd);
+      return 0;
+    }
+
+  close (fd);
 
 #if defined(__NetBSD__)
   if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)