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

« back to all changes in this revision

Viewing changes to 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
 
  char *mnt_point;
185
 
 
186
 
  mnt_point = grub_find_mount_point_from_dir (dir);
187
 
  grub_find_zpool_from_mount_point (mnt_point, &poolname, &poolfs);
188
 
  if (! poolname)
189
 
    {
190
 
      free (mnt_point);
191
 
      return NULL;
192
 
    }
193
 
 
194
 
  {
195
 
    zpool_handle_t *zpool;
196
 
    nvlist_t *nvlist;
197
 
    nvlist_t **nvlist_array;
198
 
    unsigned int nvlist_count;
199
 
 
200
 
    zpool = zpool_open (grub_get_libzfs_handle (), poolname);
201
 
    nvlist = zpool_get_config (zpool, NULL);
202
 
 
203
 
    if (nvlist_lookup_nvlist (nvlist, "vdev_tree", &nvlist) != 0)
204
 
      error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
205
 
 
206
 
    if (nvlist_lookup_nvlist_array (nvlist, "children", &nvlist_array, &nvlist_count) != 0)
207
 
      error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
208
 
 
209
 
    do
210
 
      {
211
 
        assert (nvlist_count > 0);
212
 
      } while (nvlist_lookup_nvlist_array (nvlist_array[0], "children",
213
 
                                           &nvlist_array, &nvlist_count) == 0);
214
 
 
215
 
    if (nvlist_lookup_string (nvlist_array[0], "path", &device) != 0)
216
 
      error (1, errno, "nvlist_lookup_string (\"path\")");
217
 
 
218
 
    zpool_close (zpool);
219
 
  }
220
 
 
221
 
  free (poolname);
222
 
  if (poolfs)
223
 
    free (poolfs);
224
 
 
225
 
  return device;
226
 
}
227
 
#endif
228
 
 
229
 
#ifdef __MINGW32__
230
 
 
231
 
static char *
232
 
find_root_device (const char *dir __attribute__ ((unused)),
233
 
                  dev_t dev __attribute__ ((unused)))
234
 
{
235
 
  return 0;
236
 
}
237
 
 
238
 
#elif ! defined(__CYGWIN__)
239
 
 
240
 
static char *
241
 
find_root_device (const char *dir, dev_t dev)
242
 
{
243
 
  DIR *dp;
244
 
  char *saved_cwd;
245
 
  struct dirent *ent;
246
 
 
247
 
  dp = opendir (dir);
248
 
  if (! dp)
249
 
    return 0;
250
 
 
251
 
  saved_cwd = xgetcwd ();
252
 
 
253
 
  grub_util_info ("changing current directory to %s", dir);
254
 
  if (chdir (dir) < 0)
255
 
    {
256
 
      free (saved_cwd);
257
 
      closedir (dp);
258
 
      return 0;
259
 
    }
260
 
 
261
 
  while ((ent = readdir (dp)) != 0)
262
 
    {
263
 
      struct stat st;
264
 
 
265
 
      /* Avoid:
266
 
         - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
267
 
         - dotdirs (like "/dev/.static") since they could contain duplicates.  */
268
 
      if (ent->d_name[0] == '.')
269
 
        continue;
270
 
 
271
 
      if (lstat (ent->d_name, &st) < 0)
272
 
        /* Ignore any error.  */
273
 
        continue;
274
 
 
275
 
      if (S_ISLNK (st.st_mode)) {
276
 
#ifdef __linux__
277
 
        if (strcmp (dir, "mapper") == 0) {
278
 
          /* Follow symbolic links under /dev/mapper/; the canonical name
279
 
             may be something like /dev/dm-0, but the names under
280
 
             /dev/mapper/ are more human-readable and so we prefer them if
281
 
             we can get them.  */
282
 
          if (stat (ent->d_name, &st) < 0)
283
 
            continue;
284
 
        } else
285
 
#endif /* __linux__ */
286
 
        /* Don't follow other symbolic links.  */
287
 
        continue;
288
 
      }
289
 
 
290
 
      if (S_ISDIR (st.st_mode))
291
 
        {
292
 
          /* Find it recursively.  */
293
 
          char *res;
294
 
 
295
 
          res = find_root_device (ent->d_name, dev);
296
 
 
297
 
          if (res)
298
 
            {
299
 
              if (chdir (saved_cwd) < 0)
300
 
                grub_util_error ("cannot restore the original directory");
301
 
 
302
 
              free (saved_cwd);
303
 
              closedir (dp);
304
 
              return res;
305
 
            }
306
 
        }
307
 
 
308
 
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
309
 
      if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
310
 
#else
311
 
      if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
312
 
#endif
313
 
        {
314
 
#ifdef __linux__
315
 
          /* Skip device names like /dev/dm-0, which are short-hand aliases
316
 
             to more descriptive device names, e.g. those under /dev/mapper */
317
 
          if (ent->d_name[0] == 'd' &&
318
 
              ent->d_name[1] == 'm' &&
319
 
              ent->d_name[2] == '-' &&
320
 
              ent->d_name[3] >= '0' &&
321
 
              ent->d_name[3] <= '9')
322
 
            continue;
323
 
#endif
324
 
 
325
 
          /* Found!  */
326
 
          char *res;
327
 
          char *cwd;
328
 
#if defined(__NetBSD__)
329
 
          /* Convert this block device to its character (raw) device.  */
330
 
          const char *template = "%s/r%s";
331
 
#else
332
 
          /* Keep the device name as it is.  */
333
 
          const char *template = "%s/%s";
334
 
#endif
335
 
 
336
 
          cwd = xgetcwd ();
337
 
          res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
338
 
          sprintf (res, template, cwd, ent->d_name);
339
 
          strip_extra_slashes (res);
340
 
          free (cwd);
341
 
 
342
 
          /* /dev/root is not a real block device keep looking, takes care
343
 
             of situation where root filesystem is on the same partition as
344
 
             grub files */
345
 
 
346
 
          if (strcmp(res, "/dev/root") == 0)
347
 
                continue;
348
 
 
349
 
          if (chdir (saved_cwd) < 0)
350
 
            grub_util_error ("cannot restore the original directory");
351
 
 
352
 
          free (saved_cwd);
353
 
          closedir (dp);
354
 
          return res;
355
 
        }
356
 
    }
357
 
 
358
 
  if (chdir (saved_cwd) < 0)
359
 
    grub_util_error ("cannot restore the original directory");
360
 
 
361
 
  free (saved_cwd);
362
 
  closedir (dp);
363
 
  return 0;
364
 
}
365
 
 
366
 
#else /* __CYGWIN__ */
367
 
 
368
 
/* Read drive/partition serial number from mbr/boot sector,
369
 
   return 0 on read error, ~0 on unknown serial.  */
370
 
static unsigned
371
 
get_bootsec_serial (const char *os_dev, int mbr)
372
 
{
373
 
  /* Read boot sector.  */
374
 
  int fd = open (os_dev, O_RDONLY);
375
 
  if (fd < 0)
376
 
    return 0;
377
 
  unsigned char buf[0x200];
378
 
  int n = read (fd, buf, sizeof (buf));
379
 
  close (fd);
380
 
  if (n != sizeof(buf))
381
 
    return 0;
382
 
 
383
 
  /* Check signature.  */
384
 
  if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
385
 
    return ~0;
386
 
 
387
 
  /* Serial number offset depends on boot sector type.  */
388
 
  if (mbr)
389
 
    n = 0x1b8;
390
 
  else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
391
 
    n = 0x048;
392
 
  else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
393
 
    n = 0x043;
394
 
  else if (memcmp (buf + 0x36, "FAT", 3) == 0)
395
 
    n = 0x027;
396
 
  else
397
 
    return ~0;
398
 
 
399
 
  unsigned serial = *(unsigned *)(buf + n);
400
 
  if (serial == 0)
401
 
    return ~0;
402
 
  return serial;
403
 
}
404
 
 
405
 
static char *
406
 
find_cygwin_root_device (const char *path, dev_t dev)
407
 
{
408
 
  /* No root device for /cygdrive.  */
409
 
  if (dev == (DEV_CYGDRIVE_MAJOR << 16))
410
 
    return 0;
411
 
 
412
 
  /* Convert to full POSIX and Win32 path.  */
413
 
  char fullpath[PATH_MAX], winpath[PATH_MAX];
414
 
  cygwin_conv_to_full_posix_path (path, fullpath);
415
 
  cygwin_conv_to_full_win32_path (fullpath, winpath);
416
 
 
417
 
  /* If identical, this is no real filesystem path.  */
418
 
  if (strcmp (fullpath, winpath) == 0)
419
 
    return 0;
420
 
 
421
 
  /* Check for floppy drive letter.  */
422
 
  if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
423
 
    return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
424
 
 
425
 
  /* Cygwin returns the partition serial number in stat.st_dev.
426
 
     This is never identical to the device number of the emulated
427
 
     /dev/sdXN device, so above find_root_device () does not work.
428
 
     Search the partition with the same serial in boot sector instead.  */
429
 
  char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia.  */
430
 
  int d;
431
 
  for (d = 'a'; d <= 'z'; d++)
432
 
    {
433
 
      sprintf (devpath, "/dev/sd%c", d);
434
 
      if (get_bootsec_serial (devpath, 1) == 0)
435
 
        continue;
436
 
      int p;
437
 
      for (p = 1; p <= 15; p++)
438
 
        {
439
 
          sprintf (devpath, "/dev/sd%c%d", d, p);
440
 
          unsigned ser = get_bootsec_serial (devpath, 0);
441
 
          if (ser == 0)
442
 
            break;
443
 
          if (ser != (unsigned)~0 && dev == (dev_t)ser)
444
 
            return xstrdup (devpath);
445
 
        }
446
 
    }
447
 
  return 0;
448
 
}
449
 
 
450
 
#endif /* __CYGWIN__ */
451
 
 
452
 
char *
453
 
grub_guess_root_device (const char *dir)
454
 
{
455
 
  char *os_dev;
456
 
#ifdef __GNU__
457
 
  file_t file;
458
 
  mach_port_t *ports;
459
 
  int *ints;
460
 
  loff_t *offsets;
461
 
  char *data;
462
 
  error_t err;
463
 
  mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
464
 
  size_t name_len;
465
 
 
466
 
  file = file_name_lookup (dir, 0, 0);
467
 
  if (file == MACH_PORT_NULL)
468
 
    return 0;
469
 
 
470
 
  err = file_get_storage_info (file,
471
 
                               &ports, &num_ports,
472
 
                               &ints, &num_ints,
473
 
                               &offsets, &num_offsets,
474
 
                               &data, &data_len);
475
 
 
476
 
  if (num_ints < 1)
477
 
    grub_util_error ("Storage info for `%s' does not include type", dir);
478
 
  if (ints[0] != STORAGE_DEVICE)
479
 
    grub_util_error ("Filesystem of `%s' is not stored on local disk", dir);
480
 
 
481
 
  if (num_ints < 5)
482
 
    grub_util_error ("Storage info for `%s' does not include name", dir);
483
 
  name_len = ints[4];
484
 
  if (name_len < data_len)
485
 
    grub_util_error ("Bogus name length for storage info for `%s'", dir);
486
 
  if (data[name_len - 1] != '\0')
487
 
    grub_util_error ("Storage name for `%s' not NUL-terminated", dir);
488
 
 
489
 
  os_dev = xmalloc (strlen ("/dev/") + data_len);
490
 
  memcpy (os_dev, "/dev/", strlen ("/dev/"));
491
 
  memcpy (os_dev + strlen ("/dev/"), data, data_len);
492
 
 
493
 
  if (ports && num_ports > 0)
494
 
    {
495
 
      mach_msg_type_number_t i;
496
 
      for (i = 0; i < num_ports; i++)
497
 
        {
498
 
          mach_port_t port = ports[i];
499
 
          if (port != MACH_PORT_NULL)
500
 
            mach_port_deallocate (mach_task_self(), port);
501
 
        }
502
 
      munmap ((caddr_t) ports, num_ports * sizeof (*ports));
503
 
    }
504
 
 
505
 
  if (ints && num_ints > 0)
506
 
    munmap ((caddr_t) ints, num_ints * sizeof (*ints));
507
 
  if (offsets && num_offsets > 0)
508
 
    munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
509
 
  if (data && data_len > 0)
510
 
    munmap (data, data_len);
511
 
  mach_port_deallocate (mach_task_self (), file);
512
 
#else /* !__GNU__ */
513
 
  struct stat st;
514
 
 
515
 
#ifdef __linux__
516
 
  os_dev = find_root_device_from_mountinfo (dir);
517
 
  if (os_dev)
518
 
    return os_dev;
519
 
#endif /* __linux__ */
520
 
 
521
 
#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
522
 
  os_dev = find_root_device_from_libzfs (dir);
523
 
  if (os_dev)
524
 
    return os_dev;
525
 
#endif
526
 
 
527
 
  if (stat (dir, &st) < 0)
528
 
    grub_util_error ("cannot stat `%s'", dir);
529
 
 
530
 
#ifdef __CYGWIN__
531
 
  /* Cygwin specific function.  */
532
 
  os_dev = find_cygwin_root_device (dir, st.st_dev);
533
 
 
534
 
#else
535
 
 
536
 
  /* This might be truly slow, but is there any better way?  */
537
 
  os_dev = find_root_device ("/dev", st.st_dev);
538
 
#endif
539
 
#endif /* !__GNU__ */
540
 
 
541
 
  return os_dev;
542
 
}
543
 
 
544
 
static int
545
 
grub_util_is_dmraid (const char *os_dev)
546
 
{
547
 
  if (! strncmp (os_dev, "/dev/mapper/nvidia_", 19))
548
 
    return 1;
549
 
  else if (! strncmp (os_dev, "/dev/mapper/isw_", 16))
550
 
    return 1;
551
 
  else if (! strncmp (os_dev, "/dev/mapper/hpt37x_", 19))
552
 
    return 1;
553
 
  else if (! strncmp (os_dev, "/dev/mapper/hpt45x_", 19))
554
 
    return 1;
555
 
  else if (! strncmp (os_dev, "/dev/mapper/via_", 16))
556
 
    return 1;
557
 
  else if (! strncmp (os_dev, "/dev/mapper/lsi_", 16))
558
 
    return 1;
559
 
  else if (! strncmp (os_dev, "/dev/mapper/pdc_", 16))
560
 
    return 1;
561
 
  else if (! strncmp (os_dev, "/dev/mapper/jmicron_", 20))
562
 
    return 1;
563
 
  else if (! strncmp (os_dev, "/dev/mapper/asr_", 16))
564
 
    return 1;
565
 
  else if (! strncmp (os_dev, "/dev/mapper/sil_", 16))
566
 
    return 1;
567
 
 
568
 
  return 0;
569
 
}
570
 
 
571
 
int
572
 
grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused)))
573
 
{
574
 
#ifdef __linux__
575
 
  /* Check for LVM.  */
576
 
  if (!strncmp (os_dev, "/dev/mapper/", 12)
577
 
      && ! grub_util_is_dmraid (os_dev)
578
 
      && strncmp (os_dev, "/dev/mapper/mpath", 17) != 0)
579
 
    return GRUB_DEV_ABSTRACTION_LVM;
580
 
 
581
 
  /* Check for RAID.  */
582
 
  if (!strncmp (os_dev, "/dev/md", 7))
583
 
    return GRUB_DEV_ABSTRACTION_RAID;
584
 
#endif
585
 
 
586
 
  /* No abstraction found.  */
587
 
  return GRUB_DEV_ABSTRACTION_NONE;
588
 
}
589
 
 
590
 
#ifdef __linux__
591
 
static char *
592
 
get_mdadm_name (const char *os_dev)
593
 
{
594
 
  int mdadm_pipe[2];
595
 
  pid_t mdadm_pid;
596
 
  char *name = NULL;
597
 
 
598
 
  if (pipe (mdadm_pipe) < 0)
599
 
    {
600
 
      grub_util_warn ("Unable to create pipe for mdadm: %s", strerror (errno));
601
 
      return NULL;
602
 
    }
603
 
 
604
 
  mdadm_pid = fork ();
605
 
  if (mdadm_pid < 0)
606
 
    grub_util_warn ("Unable to fork mdadm: %s", strerror (errno));
607
 
  else if (mdadm_pid == 0)
608
 
    {
609
 
      /* Child.  */
610
 
      char *argv[5];
611
 
 
612
 
      close (mdadm_pipe[0]);
613
 
      dup2 (mdadm_pipe[1], STDOUT_FILENO);
614
 
      close (mdadm_pipe[1]);
615
 
 
616
 
      /* execvp has inconvenient types, hence the casts.  None of these
617
 
         strings will actually be modified.  */
618
 
      argv[0] = (char *) "mdadm";
619
 
      argv[1] = (char *) "--detail";
620
 
      argv[2] = (char *) "--export";
621
 
      argv[3] = (char *) os_dev;
622
 
      argv[4] = NULL;
623
 
      execvp ("mdadm", argv);
624
 
      exit (127);
625
 
    }
626
 
  else
627
 
    {
628
 
      /* Parent.  Read mdadm's output.  */
629
 
      FILE *mdadm;
630
 
      char *buf = NULL;
631
 
      size_t len = 0;
632
 
 
633
 
      close (mdadm_pipe[1]);
634
 
      mdadm = fdopen (mdadm_pipe[0], "r");
635
 
      if (! mdadm)
636
 
        {
637
 
          grub_util_warn ("Unable to open stream from mdadm: %s",
638
 
                          strerror (errno));
639
 
          goto out;
640
 
        }
641
 
 
642
 
      while (getline (&buf, &len, mdadm) > 0)
643
 
        {
644
 
          if (strncmp (buf, "MD_NAME=", sizeof ("MD_NAME=") - 1) == 0)
645
 
            {
646
 
              char *name_start, *colon;
647
 
              size_t name_len;
648
 
 
649
 
              free (name);
650
 
              name_start = buf + sizeof ("MD_NAME=") - 1;
651
 
              /* Strip off the homehost if present.  */
652
 
              colon = strchr (name_start, ':');
653
 
              name = strdup (colon ? colon + 1 : name_start);
654
 
              name_len = strlen (name);
655
 
              if (name[name_len - 1] == '\n')
656
 
                name[name_len - 1] = '\0';
657
 
            }
658
 
        }
659
 
 
660
 
out:
661
 
      close (mdadm_pipe[0]);
662
 
      waitpid (mdadm_pid, NULL, 0);
663
 
    }
664
 
 
665
 
  return name;
666
 
}
667
 
#endif /* __linux__ */
668
 
 
669
 
char *
670
 
grub_util_get_grub_dev (const char *os_dev)
671
 
{
672
 
  char *grub_dev = NULL;
673
 
 
674
 
  switch (grub_util_get_dev_abstraction (os_dev))
675
 
    {
676
 
    case GRUB_DEV_ABSTRACTION_LVM:
677
 
 
678
 
      {
679
 
        unsigned short i, len;
680
 
        grub_size_t offset = sizeof ("/dev/mapper/") - 1;
681
 
 
682
 
        len = strlen (os_dev) - offset + 1;
683
 
        grub_dev = xmalloc (len);
684
 
 
685
 
        for (i = 0; i < len; i++, offset++)
686
 
          {
687
 
            grub_dev[i] = os_dev[offset];
688
 
            if (os_dev[offset] == '-' && os_dev[offset + 1] == '-')
689
 
              offset++;
690
 
          }
691
 
      }
692
 
 
693
 
      break;
694
 
 
695
 
    case GRUB_DEV_ABSTRACTION_RAID:
696
 
 
697
 
      if (os_dev[7] == '_' && os_dev[8] == 'd')
698
 
        {
699
 
          /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
700
 
 
701
 
          char *p, *q;
702
 
 
703
 
          p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
704
 
 
705
 
          q = strchr (p, 'p');
706
 
          if (q)
707
 
            *q = ',';
708
 
 
709
 
          grub_dev = xasprintf ("md%s", p);
710
 
          free (p);
711
 
        }
712
 
      else if (os_dev[7] == '/' && os_dev[8] == 'd')
713
 
        {
714
 
          /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
715
 
 
716
 
          char *p, *q;
717
 
 
718
 
          p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
719
 
 
720
 
          q = strchr (p, 'p');
721
 
          if (q)
722
 
            *q = ',';
723
 
 
724
 
          grub_dev = xasprintf ("md%s", p);
725
 
          free (p);
726
 
        }
727
 
      else if (os_dev[7] >= '0' && os_dev[7] <= '9')
728
 
        {
729
 
          char *p , *q;
730
 
 
731
 
          p = strdup (os_dev + sizeof ("/dev/md") - 1);
732
 
 
733
 
          q = strchr (p, 'p');
734
 
          if (q)
735
 
            *q = ',';
736
 
 
737
 
          grub_dev = xasprintf ("md%s", p);
738
 
          free (p);
739
 
        }
740
 
      else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
741
 
        {
742
 
          char *p , *q;
743
 
 
744
 
          p = strdup (os_dev + sizeof ("/dev/md/") - 1);
745
 
 
746
 
          q = strchr (p, 'p');
747
 
          if (q)
748
 
            *q = ',';
749
 
 
750
 
          grub_dev = xasprintf ("md%s", p);
751
 
          free (p);
752
 
        }
753
 
      else if (os_dev[7] == '/')
754
 
        {
755
 
          /* mdraid 1.x with a free name.  */
756
 
          char *p , *q;
757
 
 
758
 
          p = strdup (os_dev + sizeof ("/dev/md/") - 1);
759
 
 
760
 
          q = strchr (p, 'p');
761
 
          if (q)
762
 
            *q = ',';
763
 
 
764
 
          grub_dev = xasprintf ("md/%s", p);
765
 
          free (p);
766
 
        }
767
 
      else
768
 
        grub_util_error ("unknown kind of RAID device `%s'", os_dev);
769
 
 
770
 
#ifdef __linux__
771
 
      {
772
 
        char *mdadm_name = get_mdadm_name (os_dev);
773
 
 
774
 
        if (mdadm_name)
775
 
          {
776
 
            free (grub_dev);
777
 
            grub_dev = xasprintf ("md/%s", mdadm_name);
778
 
            free (mdadm_name);
779
 
          }
780
 
      }
781
 
#endif /* __linux__ */
782
 
 
783
 
      break;
784
 
 
785
 
    default:  /* GRUB_DEV_ABSTRACTION_NONE */
786
 
      grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
787
 
    }
788
 
 
789
 
  return grub_dev;
790
 
}
791
 
 
792
 
const char *
793
 
grub_util_check_block_device (const char *blk_dev)
794
 
{
795
 
  struct stat st;
796
 
 
797
 
  if (stat (blk_dev, &st) < 0)
798
 
    grub_util_error ("cannot stat `%s'", blk_dev);
799
 
 
800
 
  if (S_ISBLK (st.st_mode))
801
 
    return (blk_dev);
802
 
  else
803
 
    return 0;
804
 
}
805
 
 
806
 
const char *
807
 
grub_util_check_char_device (const char *blk_dev)
808
 
{
809
 
  struct stat st;
810
 
 
811
 
  if (stat (blk_dev, &st) < 0)
812
 
    grub_util_error ("cannot stat `%s'", blk_dev);
813
 
 
814
 
  if (S_ISCHR (st.st_mode))
815
 
    return (blk_dev);
816
 
  else
817
 
    return 0;
818
 
}