~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2011-02-08 11:39:26 UTC
  • mfrom: (17.6.26 experimental)
  • mto: (17.6.27 experimental)
  • mto: This revision was merged to the branch mainline in revision 104.
  • Revision ID: james.westby@ubuntu.com-20110208113926-clfs90haboyk9zip
Tags: 1.99~rc1-2
* Merge 1.98+20100804-13 and 1.98+20100804-14, updating translations:
  - Kazakh (Baurzhan Muftakhidinov / Timur Birsh).
* mkconfig_skip_dmcrypt.patch: Refer to GRUB_PRELOAD_MODULES rather than
  suggesting people write a /etc/grub.d/01_modules script (thanks, Jordan
  Uggla).
* Handle empty dir passed to grub_find_root_device_from_mountinfo; fixes
  grub-mkrelpath on btrfs subvolumes (LP: #712029).
* Add rootflags=subvol=<name> if / is on a btrfs subvolume (LP: #712029).
* Upload to unstable.

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