~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to grub-core/kern/emu/getroot.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* getroot.c - Get root device */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010  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 <config.h>
 
21
#include <sys/stat.h>
 
22
#include <sys/types.h>
 
23
#include <assert.h>
 
24
#include <fcntl.h>
 
25
#include <unistd.h>
 
26
#include <string.h>
 
27
#include <dirent.h>
 
28
#include <errno.h>
 
29
#include <error.h>
 
30
#include <stdio.h>
 
31
#include <stdlib.h>
 
32
#include <stdint.h>
 
33
#include <grub/util/misc.h>
 
34
 
 
35
#ifdef __GNU__
 
36
#include <hurd.h>
 
37
#include <hurd/lookup.h>
 
38
#include <hurd/fs.h>
 
39
#include <sys/mman.h>
 
40
#endif
 
41
 
 
42
#ifdef __linux__
 
43
# include <sys/types.h>
 
44
# include <sys/wait.h>
 
45
#endif
 
46
 
 
47
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
 
48
# include <grub/util/libzfs.h>
 
49
# include <grub/util/libnvpair.h>
 
50
#endif
 
51
 
 
52
#include <grub/mm.h>
 
53
#include <grub/misc.h>
 
54
#include <grub/emu/misc.h>
 
55
#include <grub/emu/hostdisk.h>
 
56
#include <grub/emu/getroot.h>
 
57
 
 
58
static void
 
59
strip_extra_slashes (char *dir)
 
60
{
 
61
  char *p = dir;
 
62
 
 
63
  while ((p = strchr (p, '/')) != 0)
 
64
    {
 
65
      if (p[1] == '/')
 
66
        {
 
67
          memmove (p, p + 1, strlen (p));
 
68
          continue;
 
69
        }
 
70
      else if (p[1] == '\0')
 
71
        {
 
72
          if (p > dir)
 
73
            p[0] = '\0';
 
74
          break;
 
75
        }
 
76
 
 
77
      p++;
 
78
    }
 
79
}
 
80
 
 
81
static char *
 
82
xgetcwd (void)
 
83
{
 
84
  size_t size = 10;
 
85
  char *path;
 
86
 
 
87
  path = xmalloc (size);
 
88
  while (! getcwd (path, size))
 
89
    {
 
90
      size <<= 1;
 
91
      path = xrealloc (path, size);
 
92
    }
 
93
 
 
94
  return path;
 
95
}
 
96
 
 
97
#ifdef __linux__
 
98
 
 
99
/* Statting something on a btrfs filesystem always returns a virtual device
 
100
   major/minor pair rather than the real underlying device, because btrfs
 
101
   can span multiple underlying devices (and even if it's currently only
 
102
   using a single device it can be dynamically extended onto another).  We
 
103
   can't deal with the multiple-device case yet, but in the meantime, we can
 
104
   at least cope with the single-device case by scanning
 
105
   /proc/self/mountinfo.  */
 
106
static char *
 
107
find_root_device_from_mountinfo (const char *dir)
 
108
{
 
109
  FILE *fp;
 
110
  char *buf = NULL;
 
111
  size_t len = 0;
 
112
  char *ret = NULL;
 
113
 
 
114
  fp = fopen ("/proc/self/mountinfo", "r");
 
115
  if (! fp)
 
116
    return NULL; /* fall through to other methods */
 
117
 
 
118
  while (getline (&buf, &len, fp) > 0)
 
119
    {
 
120
      int mnt_id, parent_mnt_id;
 
121
      unsigned int major, minor;
 
122
      char enc_root[PATH_MAX], enc_path[PATH_MAX];
 
123
      int count;
 
124
      size_t enc_path_len;
 
125
      const char *sep;
 
126
      char fstype[PATH_MAX], device[PATH_MAX];
 
127
      struct stat st;
 
128
 
 
129
      if (sscanf (buf, "%d %d %u:%u %s %s%n",
 
130
                  &mnt_id, &parent_mnt_id, &major, &minor, enc_root, enc_path,
 
131
                  &count) < 6)
 
132
        continue;
 
133
 
 
134
      if (strcmp (enc_root, "/") != 0)
 
135
        continue; /* only a subtree is mounted */
 
136
 
 
137
      enc_path_len = strlen (enc_path);
 
138
      if (strncmp (dir, enc_path, enc_path_len) != 0 ||
 
139
          (dir[enc_path_len] && dir[enc_path_len] != '/'))
 
140
        continue;
 
141
 
 
142
      /* This is a parent of the requested directory.  /proc/self/mountinfo
 
143
         is in mount order, so it must be the closest parent we've
 
144
         encountered so far.  If it's virtual, return its device node;
 
145
         otherwise, carry on to try to find something closer.  */
 
146
 
 
147
      free (ret);
 
148
      ret = NULL;
 
149
 
 
150
      if (major != 0)
 
151
        continue; /* not a virtual device */
 
152
 
 
153
      sep = strstr (buf + count, " - ");
 
154
      if (!sep)
 
155
        continue;
 
156
 
 
157
      sep += sizeof (" - ") - 1;
 
158
      if (sscanf (sep, "%s %s", fstype, device) != 2)
 
159
        continue;
 
160
 
 
161
      if (stat (device, &st) < 0)
 
162
        continue;
 
163
 
 
164
      if (!S_ISBLK (st.st_mode))
 
165
        continue; /* not a block device */
 
166
 
 
167
      ret = strdup (device);
 
168
    }
 
169
 
 
170
  free (buf);
 
171
  fclose (fp);
 
172
  return ret;
 
173
}
 
174
 
 
175
#endif /* __linux__ */
 
176
 
 
177
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
 
178
static char *
 
179
find_root_device_from_libzfs (const char *dir)
 
180
{
 
181
  char *device;
 
182
  char *poolname;
 
183
  char *poolfs;
 
184
 
 
185
  grub_find_zpool_from_dir (dir, &poolname, &poolfs);
 
186
  if (! poolname)
 
187
    return NULL;
 
188
 
 
189
  {
 
190
    zpool_handle_t *zpool;
 
191
    libzfs_handle_t *libzfs;
 
192
    nvlist_t *config, *vdev_tree;
 
193
    nvlist_t **children, **path;
 
194
    unsigned int nvlist_count;
 
195
    unsigned int i;
 
196
 
 
197
    libzfs = grub_get_libzfs_handle ();
 
198
    if (! libzfs)
 
199
      return NULL;
 
200
 
 
201
    zpool = zpool_open (libzfs, poolname);
 
202
    config = zpool_get_config (zpool, NULL);
 
203
 
 
204
    if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
 
205
      error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
 
206
 
 
207
    if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
 
208
      error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
 
209
    assert (nvlist_count > 0);
 
210
 
 
211
    while (nvlist_lookup_nvlist_array (children[0], "children",
 
212
                                       &children, &nvlist_count) == 0)
 
213
      assert (nvlist_count > 0);
 
214
 
 
215
    for (i = 0; i < nvlist_count; i++)
 
216
      {
 
217
        if (nvlist_lookup_string (children[i], "path", &device) != 0)
 
218
          error (1, errno, "nvlist_lookup_string (\"path\")");
 
219
 
 
220
        struct stat st;
 
221
        if (stat (device, &st) == 0)
 
222
          break;
 
223
 
 
224
        device = NULL;
 
225
      }
 
226
 
 
227
    zpool_close (zpool);
 
228
  }
 
229
 
 
230
  free (poolname);
 
231
  if (poolfs)
 
232
    free (poolfs);
 
233
 
 
234
  return device;
 
235
}
 
236
#endif
 
237
 
 
238
#ifdef __MINGW32__
 
239
 
 
240
char *
 
241
grub_find_device (const char *dir __attribute__ ((unused)),
 
242
                  dev_t dev __attribute__ ((unused)))
 
243
{
 
244
  return 0;
 
245
}
 
246
 
 
247
#elif ! defined(__CYGWIN__)
 
248
 
 
249
char *
 
250
grub_find_device (const char *dir, dev_t dev)
 
251
{
 
252
  DIR *dp;
 
253
  char *saved_cwd;
 
254
  struct dirent *ent;
 
255
 
 
256
  if (! dir)
 
257
    {
 
258
#ifdef __CYGWIN__
 
259
      return NULL;
 
260
#else
 
261
      dir = "/dev";
 
262
#endif
 
263
    }
 
264
 
 
265
  dp = opendir (dir);
 
266
  if (! dp)
 
267
    return 0;
 
268
 
 
269
  saved_cwd = xgetcwd ();
 
270
 
 
271
  grub_util_info ("changing current directory to %s", dir);
 
272
  if (chdir (dir) < 0)
 
273
    {
 
274
      free (saved_cwd);
 
275
      closedir (dp);
 
276
      return 0;
 
277
    }
 
278
 
 
279
  while ((ent = readdir (dp)) != 0)
 
280
    {
 
281
      struct stat st;
 
282
 
 
283
      /* Avoid:
 
284
         - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
 
285
         - dotdirs (like "/dev/.static") since they could contain duplicates.  */
 
286
      if (ent->d_name[0] == '.')
 
287
        continue;
 
288
 
 
289
      if (lstat (ent->d_name, &st) < 0)
 
290
        /* Ignore any error.  */
 
291
        continue;
 
292
 
 
293
      if (S_ISLNK (st.st_mode)) {
 
294
#ifdef __linux__
 
295
        if (strcmp (dir, "mapper") == 0) {
 
296
          /* Follow symbolic links under /dev/mapper/; the canonical name
 
297
             may be something like /dev/dm-0, but the names under
 
298
             /dev/mapper/ are more human-readable and so we prefer them if
 
299
             we can get them.  */
 
300
          if (stat (ent->d_name, &st) < 0)
 
301
            continue;
 
302
        } else
 
303
#endif /* __linux__ */
 
304
        /* Don't follow other symbolic links.  */
 
305
        continue;
 
306
      }
 
307
 
 
308
      if (S_ISDIR (st.st_mode))
 
309
        {
 
310
          /* Find it recursively.  */
 
311
          char *res;
 
312
 
 
313
          res = grub_find_device (ent->d_name, dev);
 
314
 
 
315
          if (res)
 
316
            {
 
317
              if (chdir (saved_cwd) < 0)
 
318
                grub_util_error ("cannot restore the original directory");
 
319
 
 
320
              free (saved_cwd);
 
321
              closedir (dp);
 
322
              return res;
 
323
            }
 
324
        }
 
325
 
 
326
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 
327
      if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
 
328
#else
 
329
      if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
 
330
#endif
 
331
        {
 
332
#ifdef __linux__
 
333
          /* Skip device names like /dev/dm-0, which are short-hand aliases
 
334
             to more descriptive device names, e.g. those under /dev/mapper */
 
335
          if (ent->d_name[0] == 'd' &&
 
336
              ent->d_name[1] == 'm' &&
 
337
              ent->d_name[2] == '-' &&
 
338
              ent->d_name[3] >= '0' &&
 
339
              ent->d_name[3] <= '9')
 
340
            continue;
 
341
#endif
 
342
 
 
343
          /* Found!  */
 
344
          char *res;
 
345
          char *cwd;
 
346
#if defined(__NetBSD__)
 
347
          /* Convert this block device to its character (raw) device.  */
 
348
          const char *template = "%s/r%s";
 
349
#else
 
350
          /* Keep the device name as it is.  */
 
351
          const char *template = "%s/%s";
 
352
#endif
 
353
 
 
354
          cwd = xgetcwd ();
 
355
          res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
 
356
          sprintf (res, template, cwd, ent->d_name);
 
357
          strip_extra_slashes (res);
 
358
          free (cwd);
 
359
 
 
360
          /* /dev/root is not a real block device keep looking, takes care
 
361
             of situation where root filesystem is on the same partition as
 
362
             grub files */
 
363
 
 
364
          if (strcmp(res, "/dev/root") == 0)
 
365
                continue;
 
366
 
 
367
          if (chdir (saved_cwd) < 0)
 
368
            grub_util_error ("cannot restore the original directory");
 
369
 
 
370
          free (saved_cwd);
 
371
          closedir (dp);
 
372
          return res;
 
373
        }
 
374
    }
 
375
 
 
376
  if (chdir (saved_cwd) < 0)
 
377
    grub_util_error ("cannot restore the original directory");
 
378
 
 
379
  free (saved_cwd);
 
380
  closedir (dp);
 
381
  return 0;
 
382
}
 
383
 
 
384
#else /* __CYGWIN__ */
 
385
 
 
386
/* Read drive/partition serial number from mbr/boot sector,
 
387
   return 0 on read error, ~0 on unknown serial.  */
 
388
static unsigned
 
389
get_bootsec_serial (const char *os_dev, int mbr)
 
390
{
 
391
  /* Read boot sector.  */
 
392
  int fd = open (os_dev, O_RDONLY);
 
393
  if (fd < 0)
 
394
    return 0;
 
395
  unsigned char buf[0x200];
 
396
  int n = read (fd, buf, sizeof (buf));
 
397
  close (fd);
 
398
  if (n != sizeof(buf))
 
399
    return 0;
 
400
 
 
401
  /* Check signature.  */
 
402
  if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
 
403
    return ~0;
 
404
 
 
405
  /* Serial number offset depends on boot sector type.  */
 
406
  if (mbr)
 
407
    n = 0x1b8;
 
408
  else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
 
409
    n = 0x048;
 
410
  else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
 
411
    n = 0x043;
 
412
  else if (memcmp (buf + 0x36, "FAT", 3) == 0)
 
413
    n = 0x027;
 
414
  else
 
415
    return ~0;
 
416
 
 
417
  unsigned serial = *(unsigned *)(buf + n);
 
418
  if (serial == 0)
 
419
    return ~0;
 
420
  return serial;
 
421
}
 
422
 
 
423
char *
 
424
grub_find_device (const char *path, dev_t dev)
 
425
{
 
426
  /* No root device for /cygdrive.  */
 
427
  if (dev == (DEV_CYGDRIVE_MAJOR << 16))
 
428
    return 0;
 
429
 
 
430
  /* Convert to full POSIX and Win32 path.  */
 
431
  char fullpath[PATH_MAX], winpath[PATH_MAX];
 
432
  cygwin_conv_to_full_posix_path (path, fullpath);
 
433
  cygwin_conv_to_full_win32_path (fullpath, winpath);
 
434
 
 
435
  /* If identical, this is no real filesystem path.  */
 
436
  if (strcmp (fullpath, winpath) == 0)
 
437
    return 0;
 
438
 
 
439
  /* Check for floppy drive letter.  */
 
440
  if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
 
441
    return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
 
442
 
 
443
  /* Cygwin returns the partition serial number in stat.st_dev.
 
444
     This is never identical to the device number of the emulated
 
445
     /dev/sdXN device, so above grub_find_device () does not work.
 
446
     Search the partition with the same serial in boot sector instead.  */
 
447
  char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia.  */
 
448
  int d;
 
449
  for (d = 'a'; d <= 'z'; d++)
 
450
    {
 
451
      sprintf (devpath, "/dev/sd%c", d);
 
452
      if (get_bootsec_serial (devpath, 1) == 0)
 
453
        continue;
 
454
      int p;
 
455
      for (p = 1; p <= 15; p++)
 
456
        {
 
457
          sprintf (devpath, "/dev/sd%c%d", d, p);
 
458
          unsigned ser = get_bootsec_serial (devpath, 0);
 
459
          if (ser == 0)
 
460
            break;
 
461
          if (ser != (unsigned)~0 && dev == (dev_t)ser)
 
462
            return xstrdup (devpath);
 
463
        }
 
464
    }
 
465
  return 0;
 
466
}
 
467
 
 
468
#endif /* __CYGWIN__ */
 
469
 
 
470
char *
 
471
grub_guess_root_device (const char *dir)
 
472
{
 
473
  char *os_dev;
 
474
#ifdef __GNU__
 
475
  file_t file;
 
476
  mach_port_t *ports;
 
477
  int *ints;
 
478
  loff_t *offsets;
 
479
  char *data;
 
480
  error_t err;
 
481
  mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
 
482
  size_t name_len;
 
483
 
 
484
  file = file_name_lookup (dir, 0, 0);
 
485
  if (file == MACH_PORT_NULL)
 
486
    return 0;
 
487
 
 
488
  err = file_get_storage_info (file,
 
489
                               &ports, &num_ports,
 
490
                               &ints, &num_ints,
 
491
                               &offsets, &num_offsets,
 
492
                               &data, &data_len);
 
493
 
 
494
  if (num_ints < 1)
 
495
    grub_util_error ("Storage info for `%s' does not include type", dir);
 
496
  if (ints[0] != STORAGE_DEVICE)
 
497
    grub_util_error ("Filesystem of `%s' is not stored on local disk", dir);
 
498
 
 
499
  if (num_ints < 5)
 
500
    grub_util_error ("Storage info for `%s' does not include name", dir);
 
501
  name_len = ints[4];
 
502
  if (name_len < data_len)
 
503
    grub_util_error ("Bogus name length for storage info for `%s'", dir);
 
504
  if (data[name_len - 1] != '\0')
 
505
    grub_util_error ("Storage name for `%s' not NUL-terminated", dir);
 
506
 
 
507
  os_dev = xmalloc (strlen ("/dev/") + data_len);
 
508
  memcpy (os_dev, "/dev/", strlen ("/dev/"));
 
509
  memcpy (os_dev + strlen ("/dev/"), data, data_len);
 
510
 
 
511
  if (ports && num_ports > 0)
 
512
    {
 
513
      mach_msg_type_number_t i;
 
514
      for (i = 0; i < num_ports; i++)
 
515
        {
 
516
          mach_port_t port = ports[i];
 
517
          if (port != MACH_PORT_NULL)
 
518
            mach_port_deallocate (mach_task_self(), port);
 
519
        }
 
520
      munmap ((caddr_t) ports, num_ports * sizeof (*ports));
 
521
    }
 
522
 
 
523
  if (ints && num_ints > 0)
 
524
    munmap ((caddr_t) ints, num_ints * sizeof (*ints));
 
525
  if (offsets && num_offsets > 0)
 
526
    munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
 
527
  if (data && data_len > 0)
 
528
    munmap (data, data_len);
 
529
  mach_port_deallocate (mach_task_self (), file);
 
530
#else /* !__GNU__ */
 
531
  struct stat st;
 
532
 
 
533
#ifdef __linux__
 
534
  os_dev = find_root_device_from_mountinfo (dir);
 
535
  if (os_dev)
 
536
    return os_dev;
 
537
#endif /* __linux__ */
 
538
 
 
539
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
 
540
  os_dev = find_root_device_from_libzfs (dir);
 
541
  if (os_dev)
 
542
    return os_dev;
 
543
#endif
 
544
 
 
545
  if (stat (dir, &st) < 0)
 
546
    grub_util_error ("cannot stat `%s'", dir);
 
547
 
 
548
#ifdef __CYGWIN__
 
549
  /* Cygwin specific function.  */
 
550
  os_dev = grub_find_device (dir, st.st_dev);
 
551
 
 
552
#else
 
553
 
 
554
  /* This might be truly slow, but is there any better way?  */
 
555
  os_dev = grub_find_device ("/dev", st.st_dev);
 
556
#endif
 
557
#endif /* !__GNU__ */
 
558
 
 
559
  return os_dev;
 
560
}
 
561
 
 
562
static int
 
563
grub_util_is_dmraid (const char *os_dev)
 
564
{
 
565
  if (! strncmp (os_dev, "/dev/mapper/nvidia_", 19))
 
566
    return 1;
 
567
  else if (! strncmp (os_dev, "/dev/mapper/isw_", 16))
 
568
    return 1;
 
569
  else if (! strncmp (os_dev, "/dev/mapper/hpt37x_", 19))
 
570
    return 1;
 
571
  else if (! strncmp (os_dev, "/dev/mapper/hpt45x_", 19))
 
572
    return 1;
 
573
  else if (! strncmp (os_dev, "/dev/mapper/via_", 16))
 
574
    return 1;
 
575
  else if (! strncmp (os_dev, "/dev/mapper/lsi_", 16))
 
576
    return 1;
 
577
  else if (! strncmp (os_dev, "/dev/mapper/pdc_", 16))
 
578
    return 1;
 
579
  else if (! strncmp (os_dev, "/dev/mapper/jmicron_", 20))
 
580
    return 1;
 
581
  else if (! strncmp (os_dev, "/dev/mapper/asr_", 16))
 
582
    return 1;
 
583
  else if (! strncmp (os_dev, "/dev/mapper/sil_", 16))
 
584
    return 1;
 
585
  else if (! strncmp (os_dev, "/dev/mapper/ddf1_", 17))
 
586
    return 1;
 
587
 
 
588
  return 0;
 
589
}
 
590
 
 
591
int
 
592
grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused)))
 
593
{
 
594
#ifdef __linux__
 
595
  /* User explicitly claims that this drive is visible by BIOS.  */
 
596
  if (grub_util_biosdisk_is_present (os_dev))
 
597
    return GRUB_DEV_ABSTRACTION_NONE;
 
598
 
 
599
  /* Check for LVM.  */
 
600
  if (!strncmp (os_dev, "/dev/mapper/", 12)
 
601
      && ! grub_util_is_dmraid (os_dev)
 
602
      && strncmp (os_dev, "/dev/mapper/mpath", 17) != 0)
 
603
    return GRUB_DEV_ABSTRACTION_LVM;
 
604
 
 
605
  /* Check for RAID.  */
 
606
  if (!strncmp (os_dev, "/dev/md", 7))
 
607
    return GRUB_DEV_ABSTRACTION_RAID;
 
608
#endif
 
609
 
 
610
  /* No abstraction found.  */
 
611
  return GRUB_DEV_ABSTRACTION_NONE;
 
612
}
 
613
 
 
614
#ifdef __linux__
 
615
static char *
 
616
get_mdadm_name (const char *os_dev)
 
617
{
 
618
  int mdadm_pipe[2];
 
619
  pid_t mdadm_pid;
 
620
  char *name = NULL;
 
621
 
 
622
  if (pipe (mdadm_pipe) < 0)
 
623
    {
 
624
      grub_util_warn ("Unable to create pipe for mdadm: %s", strerror (errno));
 
625
      return NULL;
 
626
    }
 
627
 
 
628
  mdadm_pid = fork ();
 
629
  if (mdadm_pid < 0)
 
630
    grub_util_warn ("Unable to fork mdadm: %s", strerror (errno));
 
631
  else if (mdadm_pid == 0)
 
632
    {
 
633
      /* Child.  */
 
634
      char *argv[5];
 
635
 
 
636
      close (mdadm_pipe[0]);
 
637
      dup2 (mdadm_pipe[1], STDOUT_FILENO);
 
638
      close (mdadm_pipe[1]);
 
639
 
 
640
      /* execvp has inconvenient types, hence the casts.  None of these
 
641
         strings will actually be modified.  */
 
642
      argv[0] = (char *) "mdadm";
 
643
      argv[1] = (char *) "--detail";
 
644
      argv[2] = (char *) "--export";
 
645
      argv[3] = (char *) os_dev;
 
646
      argv[4] = NULL;
 
647
      execvp ("mdadm", argv);
 
648
      exit (127);
 
649
    }
 
650
  else
 
651
    {
 
652
      /* Parent.  Read mdadm's output.  */
 
653
      FILE *mdadm;
 
654
      char *buf = NULL;
 
655
      size_t len = 0;
 
656
 
 
657
      close (mdadm_pipe[1]);
 
658
      mdadm = fdopen (mdadm_pipe[0], "r");
 
659
      if (! mdadm)
 
660
        {
 
661
          grub_util_warn ("Unable to open stream from mdadm: %s",
 
662
                          strerror (errno));
 
663
          goto out;
 
664
        }
 
665
 
 
666
      while (getline (&buf, &len, mdadm) > 0)
 
667
        {
 
668
          if (strncmp (buf, "MD_NAME=", sizeof ("MD_NAME=") - 1) == 0)
 
669
            {
 
670
              char *name_start, *colon;
 
671
              size_t name_len;
 
672
 
 
673
              free (name);
 
674
              name_start = buf + sizeof ("MD_NAME=") - 1;
 
675
              /* Strip off the homehost if present.  */
 
676
              colon = strchr (name_start, ':');
 
677
              name = strdup (colon ? colon + 1 : name_start);
 
678
              name_len = strlen (name);
 
679
              if (name[name_len - 1] == '\n')
 
680
                name[name_len - 1] = '\0';
 
681
            }
 
682
        }
 
683
 
 
684
out:
 
685
      close (mdadm_pipe[0]);
 
686
      waitpid (mdadm_pid, NULL, 0);
 
687
    }
 
688
 
 
689
  return name;
 
690
}
 
691
#endif /* __linux__ */
 
692
 
 
693
char *
 
694
grub_util_get_grub_dev (const char *os_dev)
 
695
{
 
696
  char *grub_dev = NULL;
 
697
 
 
698
  switch (grub_util_get_dev_abstraction (os_dev))
 
699
    {
 
700
    case GRUB_DEV_ABSTRACTION_LVM:
 
701
 
 
702
      {
 
703
        unsigned short i, len;
 
704
        grub_size_t offset = sizeof ("/dev/mapper/") - 1;
 
705
 
 
706
        len = strlen (os_dev) - offset + 1;
 
707
        grub_dev = xmalloc (len);
 
708
 
 
709
        for (i = 0; i < len; i++, offset++)
 
710
          {
 
711
            grub_dev[i] = os_dev[offset];
 
712
            if (os_dev[offset] == '-' && os_dev[offset + 1] == '-')
 
713
              offset++;
 
714
          }
 
715
      }
 
716
 
 
717
      break;
 
718
 
 
719
    case GRUB_DEV_ABSTRACTION_RAID:
 
720
 
 
721
      if (os_dev[7] == '_' && os_dev[8] == 'd')
 
722
        {
 
723
          /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
 
724
 
 
725
          char *p, *q;
 
726
 
 
727
          p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
 
728
 
 
729
          q = strchr (p, 'p');
 
730
          if (q)
 
731
            *q = ',';
 
732
 
 
733
          grub_dev = xasprintf ("md%s", p);
 
734
          free (p);
 
735
        }
 
736
      else if (os_dev[7] == '/' && os_dev[8] == 'd')
 
737
        {
 
738
          /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
 
739
 
 
740
          char *p, *q;
 
741
 
 
742
          p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
 
743
 
 
744
          q = strchr (p, 'p');
 
745
          if (q)
 
746
            *q = ',';
 
747
 
 
748
          grub_dev = xasprintf ("md%s", p);
 
749
          free (p);
 
750
        }
 
751
      else if (os_dev[7] >= '0' && os_dev[7] <= '9')
 
752
        {
 
753
          char *p , *q;
 
754
 
 
755
          p = strdup (os_dev + sizeof ("/dev/md") - 1);
 
756
 
 
757
          q = strchr (p, 'p');
 
758
          if (q)
 
759
            *q = ',';
 
760
 
 
761
          grub_dev = xasprintf ("md%s", p);
 
762
          free (p);
 
763
        }
 
764
      else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
 
765
        {
 
766
          char *p , *q;
 
767
 
 
768
          p = strdup (os_dev + sizeof ("/dev/md/") - 1);
 
769
 
 
770
          q = strchr (p, 'p');
 
771
          if (q)
 
772
            *q = ',';
 
773
 
 
774
          grub_dev = xasprintf ("md%s", p);
 
775
          free (p);
 
776
        }
 
777
      else if (os_dev[7] == '/')
 
778
        {
 
779
          /* mdraid 1.x with a free name.  */
 
780
          char *p , *q;
 
781
 
 
782
          p = strdup (os_dev + sizeof ("/dev/md/") - 1);
 
783
 
 
784
          q = strchr (p, 'p');
 
785
          if (q)
 
786
            *q = ',';
 
787
 
 
788
          grub_dev = xasprintf ("md/%s", p);
 
789
          free (p);
 
790
        }
 
791
      else
 
792
        grub_util_error ("unknown kind of RAID device `%s'", os_dev);
 
793
 
 
794
#ifdef __linux__
 
795
      {
 
796
        char *mdadm_name = get_mdadm_name (os_dev);
 
797
 
 
798
        if (mdadm_name)
 
799
          {
 
800
            free (grub_dev);
 
801
            grub_dev = xasprintf ("md/%s", mdadm_name);
 
802
            free (mdadm_name);
 
803
          }
 
804
      }
 
805
#endif /* __linux__ */
 
806
 
 
807
      break;
 
808
 
 
809
    default:  /* GRUB_DEV_ABSTRACTION_NONE */
 
810
      grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
 
811
    }
 
812
 
 
813
  return grub_dev;
 
814
}
 
815
 
 
816
const char *
 
817
grub_util_check_block_device (const char *blk_dev)
 
818
{
 
819
  struct stat st;
 
820
 
 
821
  if (stat (blk_dev, &st) < 0)
 
822
    grub_util_error ("cannot stat `%s'", blk_dev);
 
823
 
 
824
  if (S_ISBLK (st.st_mode))
 
825
    return (blk_dev);
 
826
  else
 
827
    return 0;
 
828
}
 
829
 
 
830
const char *
 
831
grub_util_check_char_device (const char *blk_dev)
 
832
{
 
833
  struct stat st;
 
834
 
 
835
  if (stat (blk_dev, &st) < 0)
 
836
    grub_util_error ("cannot stat `%s'", blk_dev);
 
837
 
 
838
  if (S_ISCHR (st.st_mode))
 
839
    return (blk_dev);
 
840
  else
 
841
    return 0;
 
842
}