~hamo/ubuntu/precise/grub2/grub2.hi_res

« 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, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

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
}