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

« back to all changes in this revision

Viewing changes to grub-core/osdep/linux/getroot.c

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2014-01-16 15:18:04 UTC
  • mfrom: (17.6.38 experimental)
  • Revision ID: package-import@ubuntu.com-20140116151804-3foouk7fpqcq3sxx
Tags: 2.02~beta2-2
* Convert patch handling to git-dpm.
* Add bi-endian support to ELF parser (Tomohiro B Berry).
* Adjust restore_mkdevicemap.patch to mark get_kfreebsd_version as static,
  to appease "gcc -Werror=missing-prototypes".
* Cherry-pick from upstream:
  - Change grub-macbless' manual page section to 8.
* Install grub-glue-efi, grub-macbless, grub-render-label, and
  grub-syslinux2cfg.
* grub-shell: Pass -no-pad to xorriso when building floppy images.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  GRUB  --  GRand Unified Bootloader
 
3
 *  Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011,2012,2013  Free Software Foundation, Inc.
 
4
 *
 
5
 *  GRUB is free software: you can redistribute it and/or modify
 
6
 *  it under the terms of the GNU General Public License as published by
 
7
 *  the Free Software Foundation, either version 3 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  GRUB is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#include <config-util.h>
 
20
#include <config.h>
 
21
 
 
22
#include <sys/stat.h>
 
23
#include <sys/types.h>
 
24
#include <assert.h>
 
25
#include <fcntl.h>
 
26
#include <unistd.h>
 
27
#include <string.h>
 
28
#include <dirent.h>
 
29
#include <errno.h>
 
30
#include <error.h>
 
31
#include <stdio.h>
 
32
#include <stdlib.h>
 
33
#include <stdint.h>
 
34
#ifdef HAVE_LIMITS_H
 
35
#include <limits.h>
 
36
#endif
 
37
 
 
38
#include <grub/types.h>
 
39
#include <sys/ioctl.h>         /* ioctl */
 
40
#include <sys/mount.h>
 
41
 
 
42
#include <grub/util/misc.h>
 
43
 
 
44
#include <grub/mm.h>
 
45
#include <grub/misc.h>
 
46
#include <grub/emu/misc.h>
 
47
#include <grub/emu/hostdisk.h>
 
48
#include <grub/emu/getroot.h>
 
49
 
 
50
#include <sys/wait.h>
 
51
 
 
52
#include <linux/types.h>
 
53
#include <linux/major.h>
 
54
#include <linux/raid/md_p.h>
 
55
#include <linux/raid/md_u.h>
 
56
#include <grub/i18n.h>
 
57
#include <grub/emu/exec.h>
 
58
#include <grub/btrfs.h>
 
59
 
 
60
#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
 
61
 
 
62
/* Defines taken from btrfs/ioctl.h.  */
 
63
 
 
64
struct btrfs_ioctl_dev_info_args
 
65
{
 
66
  grub_uint64_t devid;
 
67
  grub_uint8_t uuid[16];
 
68
  grub_uint64_t bytes_used;
 
69
  grub_uint64_t total_bytes;
 
70
  grub_uint64_t unused[379];
 
71
  grub_uint8_t path[1024];
 
72
};
 
73
 
 
74
struct btrfs_ioctl_fs_info_args
 
75
{
 
76
  grub_uint64_t max_id;
 
77
  grub_uint64_t num_devices;
 
78
  grub_uint8_t fsid[16];
 
79
  grub_uint64_t reserved[124];
 
80
};
 
81
 
 
82
struct btrfs_ioctl_ino_lookup_args
 
83
{
 
84
  grub_uint64_t treeid;
 
85
  grub_uint64_t objectid;
 
86
  char name[4080];
 
87
};
 
88
 
 
89
struct btrfs_ioctl_search_key
 
90
{
 
91
  grub_uint64_t tree_id;
 
92
  grub_uint64_t min_objectid;
 
93
  grub_uint64_t max_objectid;
 
94
  grub_uint64_t min_offset;
 
95
  grub_uint64_t max_offset;
 
96
  grub_uint64_t min_transid;
 
97
  grub_uint64_t max_transid;
 
98
  grub_uint32_t min_type;
 
99
  grub_uint32_t max_type;
 
100
  grub_uint32_t nr_items;
 
101
  grub_uint32_t unused[9];
 
102
};
 
103
 
 
104
struct btrfs_ioctl_search_args {
 
105
  struct btrfs_ioctl_search_key key;
 
106
  grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key))
 
107
                    / sizeof (grub_uint64_t)];
 
108
};
 
109
 
 
110
#define BTRFS_IOC_TREE_SEARCH _IOWR(0x94, 17, \
 
111
                                   struct btrfs_ioctl_search_args)
 
112
#define BTRFS_IOC_INO_LOOKUP _IOWR(0x94, 18, \
 
113
                                   struct btrfs_ioctl_ino_lookup_args)
 
114
#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \
 
115
                                 struct btrfs_ioctl_dev_info_args)
 
116
#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
 
117
                               struct btrfs_ioctl_fs_info_args)
 
118
 
 
119
static int
 
120
grub_util_is_imsm (const char *os_dev);
 
121
 
 
122
 
 
123
#define ESCAPED_PATH_MAX (4 * PATH_MAX)
 
124
struct mountinfo_entry
 
125
{
 
126
  int id;
 
127
  int major, minor;
 
128
  char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
 
129
  char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
 
130
};
 
131
 
 
132
static char **
 
133
grub_util_raid_getmembers (const char *name, int bootable)
 
134
{
 
135
  int fd, ret, i, j;
 
136
  char **devicelist;
 
137
  mdu_version_t version;
 
138
  mdu_array_info_t info;
 
139
  mdu_disk_info_t disk;
 
140
 
 
141
  fd = open (name, O_RDONLY);
 
142
 
 
143
  if (fd == -1)
 
144
    grub_util_error (_("cannot open `%s': %s"), name, strerror (errno));
 
145
 
 
146
  ret = ioctl (fd, RAID_VERSION, &version);
 
147
  if (ret != 0)
 
148
    grub_util_error (_("ioctl RAID_VERSION error: %s"), strerror (errno));
 
149
 
 
150
  if ((version.major != 0 || version.minor != 90)
 
151
      && (version.major != 1 || version.minor != 0)
 
152
      && (version.major != 1 || version.minor != 1)
 
153
      && (version.major != 1 || version.minor != 2))
 
154
    grub_util_error (_("unsupported RAID version: %d.%d"),
 
155
                     version.major, version.minor);
 
156
 
 
157
  if (bootable && (version.major != 0 || version.minor != 90))
 
158
    grub_util_error (_("unsupported RAID version: %d.%d"),
 
159
                     version.major, version.minor);
 
160
 
 
161
  ret = ioctl (fd, GET_ARRAY_INFO, &info);
 
162
  if (ret != 0)
 
163
    grub_util_error (_("ioctl GET_ARRAY_INFO error: %s"), strerror (errno));
 
164
 
 
165
  devicelist = xmalloc ((info.nr_disks + 1) * sizeof (char *));
 
166
 
 
167
  for (i = 0, j = 0; j < info.nr_disks; i++)
 
168
    {
 
169
      disk.number = i;
 
170
      ret = ioctl (fd, GET_DISK_INFO, &disk);
 
171
      if (ret != 0)
 
172
        grub_util_error (_("ioctl GET_DISK_INFO error: %s"), strerror (errno));
 
173
      
 
174
      if (disk.state & (1 << MD_DISK_REMOVED))
 
175
        continue;
 
176
 
 
177
      if (disk.state & (1 << MD_DISK_ACTIVE))
 
178
        devicelist[j] = grub_find_device (NULL,
 
179
                                          makedev (disk.major, disk.minor));
 
180
      else
 
181
        devicelist[j] = NULL;
 
182
      j++;
 
183
    }
 
184
 
 
185
  devicelist[j] = NULL;
 
186
 
 
187
  close (fd);
 
188
 
 
189
  return devicelist;
 
190
}
 
191
 
 
192
/* Statting something on a btrfs filesystem always returns a virtual device
 
193
   major/minor pair rather than the real underlying device, because btrfs
 
194
   can span multiple underlying devices (and even if it's currently only
 
195
   using a single device it can be dynamically extended onto another).  We
 
196
   can't deal with the multiple-device case yet, but in the meantime, we can
 
197
   at least cope with the single-device case by scanning
 
198
   /proc/self/mountinfo.  */
 
199
static void
 
200
unescape (char *str)
 
201
{
 
202
  char *optr;
 
203
  const char *iptr;
 
204
  for (iptr = optr = str; *iptr; optr++)
 
205
    {
 
206
      if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
 
207
          && iptr[2] >= '0' && iptr[2] < '8'
 
208
          && iptr[3] >= '0' && iptr[3] < '8')
 
209
        {
 
210
          *optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
 
211
                   | (iptr[3] - '0'));
 
212
          iptr += 4;
 
213
        }
 
214
      else
 
215
        *optr = *iptr++;
 
216
    }
 
217
  *optr = 0;
 
218
}
 
219
 
 
220
static char **
 
221
grub_find_root_devices_from_btrfs (const char *dir)
 
222
{
 
223
  int fd;
 
224
  struct btrfs_ioctl_fs_info_args fsi;
 
225
  int i, j = 0;
 
226
  char **ret;
 
227
 
 
228
  fd = open (dir, 0);
 
229
  if (!fd)
 
230
    return NULL;
 
231
 
 
232
  if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
 
233
    {
 
234
      close (fd);
 
235
      return NULL;
 
236
    }
 
237
 
 
238
  ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
 
239
 
 
240
  for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
 
241
    {
 
242
      struct btrfs_ioctl_dev_info_args devi;
 
243
      memset (&devi, 0, sizeof (devi));
 
244
      devi.devid = i;
 
245
      if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0)
 
246
        {
 
247
          close (fd);
 
248
          free (ret);
 
249
          return NULL;
 
250
        }
 
251
      ret[j++] = xstrdup ((char *) devi.path);
 
252
      if (j >= fsi.num_devices)
 
253
        break;
 
254
    }
 
255
  close (fd);
 
256
  ret[j] = 0;
 
257
  return ret;
 
258
}
 
259
 
 
260
static char *
 
261
get_btrfs_fs_prefix (const char *mount_path)
 
262
{
 
263
  struct btrfs_ioctl_ino_lookup_args args;
 
264
  struct stat st;
 
265
  int fd;
 
266
  grub_uint64_t tree_id, inode_id;
 
267
  char *ret = NULL;
 
268
 
 
269
  fd = open (mount_path, O_RDONLY);
 
270
          
 
271
  if (fd < 0)
 
272
    return NULL;
 
273
  memset (&args, 0, sizeof(args));
 
274
  args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID;
 
275
  
 
276
  if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
 
277
    return NULL;
 
278
  tree_id = args.treeid;
 
279
 
 
280
  if (fstat (fd, &st) < 0)
 
281
    return NULL;
 
282
  inode_id = st.st_ino;
 
283
 
 
284
  while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID
 
285
         || inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID)
 
286
    {
 
287
      const char *name;
 
288
      size_t namelen;
 
289
      struct btrfs_ioctl_search_args sargs;
 
290
      char *old;
 
291
 
 
292
      memset (&sargs, 0, sizeof(sargs));
 
293
 
 
294
      if (inode_id == GRUB_BTRFS_TREE_ROOT_OBJECTID)
 
295
        {
 
296
          struct grub_btrfs_root_backref *br;
 
297
 
 
298
          sargs.key.tree_id = 1;
 
299
          sargs.key.min_objectid = tree_id;
 
300
          sargs.key.max_objectid = tree_id;
 
301
 
 
302
          sargs.key.min_offset = 0;
 
303
          sargs.key.max_offset = ~0ULL;
 
304
          sargs.key.min_transid = 0;
 
305
          sargs.key.max_transid = ~0ULL;
 
306
          sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
 
307
          sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF;
 
308
 
 
309
          sargs.key.nr_items = 1;
 
310
 
 
311
          if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
 
312
            return NULL;
 
313
 
 
314
          if (sargs.key.nr_items == 0)
 
315
            return NULL;
 
316
      
 
317
          tree_id = sargs.buf[2];
 
318
          br = (struct grub_btrfs_root_backref *) (sargs.buf + 4);
 
319
          inode_id = br->inode_id;
 
320
          name = br->name;
 
321
          namelen = br->n;
 
322
        }
 
323
      else
 
324
        {
 
325
          struct grub_btrfs_inode_ref *ir;
 
326
 
 
327
          sargs.key.tree_id = tree_id;
 
328
          sargs.key.min_objectid = inode_id;
 
329
          sargs.key.max_objectid = inode_id;
 
330
 
 
331
          sargs.key.min_offset = 0;
 
332
          sargs.key.max_offset = ~0ULL;
 
333
          sargs.key.min_transid = 0;
 
334
          sargs.key.max_transid = ~0ULL;
 
335
          sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
 
336
          sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
 
337
 
 
338
          if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
 
339
            return NULL;
 
340
 
 
341
          if (sargs.key.nr_items == 0)
 
342
            return NULL;
 
343
 
 
344
          inode_id = sargs.buf[2];
 
345
 
 
346
          ir = (struct grub_btrfs_inode_ref *) (sargs.buf + 4);
 
347
          name = ir->name;
 
348
          namelen = ir->n;
 
349
        }
 
350
      old = ret;
 
351
      ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2);
 
352
      ret[0] = '/';
 
353
      memcpy (ret + 1, name, namelen);
 
354
      if (old)
 
355
        {
 
356
          strcpy (ret + 1 + namelen, old);
 
357
          free (old);
 
358
        }
 
359
      else
 
360
        ret[1+namelen] = '\0';
 
361
    }
 
362
  if (!ret)
 
363
    return xstrdup ("/");
 
364
  return ret;
 
365
}
 
366
 
 
367
 
 
368
char **
 
369
grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
 
370
{
 
371
  FILE *fp;
 
372
  char *buf = NULL;
 
373
  size_t len = 0;
 
374
  grub_size_t entry_len = 0, entry_max = 4;
 
375
  struct mountinfo_entry *entries;
 
376
  struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
 
377
  int i;
 
378
 
 
379
  if (! *dir)
 
380
    dir = "/";
 
381
  if (relroot)
 
382
    *relroot = NULL;
 
383
 
 
384
  fp = grub_util_fopen ("/proc/self/mountinfo", "r");
 
385
  if (! fp)
 
386
    return NULL; /* fall through to other methods */
 
387
 
 
388
  entries = xmalloc (entry_max * sizeof (*entries));
 
389
 
 
390
  /* First, build a list of relevant visible mounts.  */
 
391
  while (getline (&buf, &len, fp) > 0)
 
392
    {
 
393
      struct mountinfo_entry entry;
 
394
      int count;
 
395
      size_t enc_path_len;
 
396
      const char *sep;
 
397
 
 
398
      if (sscanf (buf, "%d %d %u:%u %s %s%n",
 
399
                  &entry.id, &parent_entry.id, &entry.major, &entry.minor,
 
400
                  entry.enc_root, entry.enc_path, &count) < 6)
 
401
        continue;
 
402
 
 
403
      unescape (entry.enc_root);
 
404
      unescape (entry.enc_path);
 
405
 
 
406
      enc_path_len = strlen (entry.enc_path);
 
407
      /* Check that enc_path is a prefix of dir.  The prefix must either be
 
408
         the entire string, or end with a slash, or be immediately followed
 
409
         by a slash.  */
 
410
      if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
 
411
          (enc_path_len && dir[enc_path_len - 1] != '/' &&
 
412
           dir[enc_path_len] && dir[enc_path_len] != '/'))
 
413
        continue;
 
414
 
 
415
      sep = strstr (buf + count, " - ");
 
416
      if (!sep)
 
417
        continue;
 
418
 
 
419
      sep += sizeof (" - ") - 1;
 
420
      if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
 
421
        continue;
 
422
 
 
423
      unescape (entry.device);
 
424
 
 
425
      /* Using the mount IDs, find out where this fits in the list of
 
426
         visible mount entries we've seen so far.  There are three
 
427
         interesting cases.  Firstly, it may be inserted at the end: this is
 
428
         the usual case of /foo/bar being mounted after /foo.  Secondly, it
 
429
         may be inserted at the start: for example, this can happen for
 
430
         filesystems that are mounted before / and later moved under it.
 
431
         Thirdly, it may occlude part or all of the existing filesystem
 
432
         tree, in which case the end of the list needs to be pruned and this
 
433
         new entry will be inserted at the end.  */
 
434
      if (entry_len >= entry_max)
 
435
        {
 
436
          entry_max <<= 1;
 
437
          entries = xrealloc (entries, entry_max * sizeof (*entries));
 
438
        }
 
439
 
 
440
      if (!entry_len)
 
441
        {
 
442
          /* Initialise list.  */
 
443
          entry_len = 2;
 
444
          entries[0] = parent_entry;
 
445
          entries[1] = entry;
 
446
        }
 
447
      else
 
448
        {
 
449
          for (i = entry_len - 1; i >= 0; i--)
 
450
            {
 
451
              if (entries[i].id == parent_entry.id)
 
452
                {
 
453
                  /* Insert at end, pruning anything previously above this.  */
 
454
                  entry_len = i + 2;
 
455
                  entries[i + 1] = entry;
 
456
                  break;
 
457
                }
 
458
              else if (i == 0 && entries[i].id == entry.id)
 
459
                {
 
460
                  /* Insert at start.  */
 
461
                  entry_len++;
 
462
                  memmove (entries + 1, entries,
 
463
                           (entry_len - 1) * sizeof (*entries));
 
464
                  entries[0] = parent_entry;
 
465
                  entries[1] = entry;
 
466
                  break;
 
467
                }
 
468
            }
 
469
        }
 
470
    }
 
471
 
 
472
  /* Now scan visible mounts for the ones we're interested in.  */
 
473
  for (i = entry_len - 1; i >= 0; i--)
 
474
    {
 
475
      char **ret = NULL;
 
476
      char *fs_prefix = NULL;
 
477
      if (!*entries[i].device)
 
478
        continue;
 
479
 
 
480
      if (grub_strcmp (entries[i].fstype, "fuse.zfs") == 0
 
481
          || grub_strcmp (entries[i].fstype, "zfs") == 0)
 
482
        {
 
483
          char *slash;
 
484
          slash = strchr (entries[i].device, '/');
 
485
          if (slash)
 
486
            *slash = 0;
 
487
          ret = grub_util_find_root_devices_from_poolname (entries[i].device);
 
488
          if (slash)
 
489
            *slash = '/';
 
490
          if (relroot)
 
491
            {
 
492
              if (!slash)
 
493
                fs_prefix = xasprintf ("/@%s", entries[i].enc_root);
 
494
              else if (strchr (slash + 1, '@'))
 
495
                fs_prefix = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
 
496
              else
 
497
                fs_prefix = xasprintf ("/%s@%s", slash + 1,
 
498
                                       entries[i].enc_root);
 
499
            }
 
500
        }
 
501
      else if (grub_strcmp (entries[i].fstype, "btrfs") == 0)
 
502
        {
 
503
          ret = grub_find_root_devices_from_btrfs (dir);
 
504
          fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
 
505
        }
 
506
      if (!ret)
 
507
        {
 
508
          ret = xmalloc (2 * sizeof (ret[0]));
 
509
          ret[0] = strdup (entries[i].device);
 
510
          ret[1] = 0;
 
511
        }
 
512
      if (!fs_prefix)
 
513
        fs_prefix = entries[i].enc_root;
 
514
      if (relroot)
 
515
        {
 
516
          char *ptr;
 
517
          grub_size_t enc_root_len = strlen (fs_prefix);
 
518
          grub_size_t enc_path_len = strlen (entries[i].enc_path);
 
519
          grub_size_t dir_strlen = strlen (dir);
 
520
          *relroot = xmalloc (enc_root_len +
 
521
                              2 + dir_strlen);
 
522
          ptr = grub_stpcpy (*relroot, fs_prefix);
 
523
          if (dir_strlen > enc_path_len)
 
524
            {
 
525
              while (ptr > *relroot && *(ptr - 1) == '/')
 
526
                ptr--;
 
527
              if (dir[enc_path_len] != '/')
 
528
                *ptr++ = '/';
 
529
              ptr = grub_stpcpy (ptr, dir + enc_path_len);
 
530
            }
 
531
          *ptr = 0;
 
532
        }
 
533
      if (fs_prefix != entries[i].enc_root)
 
534
        free (fs_prefix);
 
535
      free (buf);
 
536
      free (entries);
 
537
      fclose (fp);
 
538
      return ret;
 
539
    }
 
540
 
 
541
  free (buf);
 
542
  free (entries);
 
543
  fclose (fp);
 
544
  return NULL;
 
545
}
 
546
 
 
547
static char *
 
548
get_mdadm_uuid (const char *os_dev)
 
549
{
 
550
  const char *argv[5];
 
551
  int fd;
 
552
  pid_t pid;
 
553
  FILE *mdadm;
 
554
  char *buf = NULL;
 
555
  size_t len = 0;
 
556
  char *name = NULL;
 
557
 
 
558
  argv[0] = "mdadm";
 
559
  argv[1] = "--detail";
 
560
  argv[2] = "--export";
 
561
  argv[3] = os_dev;
 
562
  argv[4] = NULL;
 
563
 
 
564
  pid = grub_util_exec_pipe (argv, &fd);
 
565
 
 
566
  if (!pid)
 
567
    return NULL;
 
568
 
 
569
  /* Parent.  Read mdadm's output.  */
 
570
  mdadm = fdopen (fd, "r");
 
571
  if (! mdadm)
 
572
    {
 
573
      grub_util_warn (_("Unable to open stream from %s: %s"),
 
574
                      "mdadm", strerror (errno));
 
575
      goto out;
 
576
    }
 
577
 
 
578
  while (getline (&buf, &len, mdadm) > 0)
 
579
    {
 
580
      if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
 
581
        {
 
582
          char *name_start, *ptri, *ptro;
 
583
          
 
584
          free (name);
 
585
          name_start = buf + sizeof ("MD_UUID=") - 1;
 
586
          ptro = name = xmalloc (strlen (name_start) + 1);
 
587
          for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
 
588
               ptri++)
 
589
            if ((*ptri >= '0' && *ptri <= '9')
 
590
                || (*ptri >= 'a' && *ptri <= 'f')
 
591
                || (*ptri >= 'A' && *ptri <= 'F'))
 
592
              *ptro++ = *ptri;
 
593
          *ptro = 0;
 
594
        }
 
595
    }
 
596
 
 
597
out:
 
598
  close (fd);
 
599
  waitpid (pid, NULL, 0);
 
600
  free (buf);
 
601
 
 
602
  return name;
 
603
}
 
604
 
 
605
static int
 
606
grub_util_is_imsm (const char *os_dev)
 
607
{
 
608
  int retry;
 
609
  int is_imsm = 0;
 
610
  int container_seen = 0;
 
611
  const char *dev = os_dev;
 
612
 
 
613
  do
 
614
    {
 
615
      const char *argv[5];
 
616
      int fd;
 
617
      pid_t pid;
 
618
      FILE *mdadm;
 
619
      char *buf = NULL;
 
620
      size_t len = 0;
 
621
 
 
622
      retry = 0; /* We'll do one more pass if device is part of container */
 
623
 
 
624
      argv[0] = "mdadm";
 
625
      argv[1] = "--detail";
 
626
      argv[2] = "--export";
 
627
      argv[3] = dev;
 
628
      argv[4] = NULL;
 
629
 
 
630
      pid = grub_util_exec_pipe (argv, &fd);
 
631
 
 
632
      if (!pid)
 
633
        {
 
634
          if (dev != os_dev)
 
635
            free ((void *) dev);
 
636
          return 0;
 
637
        }
 
638
 
 
639
      /* Parent.  Read mdadm's output.  */
 
640
      mdadm = fdopen (fd, "r");
 
641
      if (! mdadm)
 
642
        {
 
643
          grub_util_warn (_("Unable to open stream from %s: %s"),
 
644
                          "mdadm", strerror (errno));
 
645
          close (fd);
 
646
          waitpid (pid, NULL, 0);
 
647
          if (dev != os_dev)
 
648
            free ((void *) dev);
 
649
          return 0;
 
650
        }
 
651
 
 
652
      while (getline (&buf, &len, mdadm) > 0)
 
653
        {
 
654
          if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0
 
655
              && !container_seen)
 
656
            {
 
657
              char *newdev, *ptr;
 
658
              newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1);
 
659
              ptr = newdev + strlen (newdev) - 1;
 
660
              for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--);
 
661
              ptr[1] = 0;
 
662
              grub_util_info ("Container of %s is %s", dev, newdev);
 
663
              dev = newdev;
 
664
              container_seen = retry = 1;
 
665
              break;
 
666
            }
 
667
          if (strncmp (buf, "MD_METADATA=imsm",
 
668
                       sizeof ("MD_METADATA=imsm") - 1) == 0)
 
669
            {
 
670
              is_imsm = 1;
 
671
              grub_util_info ("%s is imsm", dev);             
 
672
              break;
 
673
            }
 
674
        }
 
675
 
 
676
      free (buf);
 
677
      close (fd);
 
678
      waitpid (pid, NULL, 0);
 
679
    }
 
680
  while (retry);
 
681
 
 
682
  if (dev != os_dev)
 
683
    free ((void *) dev);
 
684
  return is_imsm;
 
685
}
 
686
 
 
687
char *
 
688
grub_util_part_to_disk (const char *os_dev, struct stat *st,
 
689
                        int *is_part)
 
690
{
 
691
  char *path = xmalloc (PATH_MAX);
 
692
 
 
693
  if (! S_ISBLK (st->st_mode))
 
694
    {
 
695
      *is_part = 0;
 
696
      return xstrdup (os_dev);
 
697
    }
 
698
 
 
699
  if (! realpath (os_dev, path))
 
700
    return NULL;
 
701
 
 
702
  if (strncmp ("/dev/", path, 5) == 0)
 
703
    {
 
704
      char *p = path + 5;
 
705
 
 
706
      /* If this is an IDE disk.  */
 
707
      if (strncmp ("ide/", p, 4) == 0)
 
708
        {
 
709
          p = strstr (p, "part");
 
710
          if (p)
 
711
            {
 
712
              *is_part = 1;
 
713
              strcpy (p, "disc");
 
714
            }
 
715
 
 
716
          return path;
 
717
        }
 
718
 
 
719
      /* If this is a SCSI disk.  */
 
720
      if (strncmp ("scsi/", p, 5) == 0)
 
721
        {
 
722
          p = strstr (p, "part");
 
723
          if (p)
 
724
            {
 
725
              *is_part = 1;
 
726
              strcpy (p, "disc");
 
727
            }
 
728
 
 
729
          return path;
 
730
        }
 
731
 
 
732
      /* If this is a DAC960 disk.  */
 
733
      if (strncmp ("rd/c", p, 4) == 0)
 
734
        {
 
735
          /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
 
736
          p = strchr (p, 'p');
 
737
          if (p)
 
738
            {
 
739
              *is_part = 1;
 
740
              *p = '\0';
 
741
            }
 
742
 
 
743
          return path;
 
744
        }
 
745
 
 
746
      /* If this is a Mylex AcceleRAID Array.  */
 
747
      if (strncmp ("rs/c", p, 4) == 0)
 
748
        {
 
749
          /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
 
750
          p = strchr (p, 'p');
 
751
          if (p)
 
752
            {
 
753
              *is_part = 1;
 
754
              *p = '\0';
 
755
            }
 
756
 
 
757
          return path;
 
758
        }
 
759
      /* If this is a CCISS disk.  */
 
760
      if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
 
761
        {
 
762
          /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
 
763
          p = strchr (p, 'p');
 
764
          if (p)
 
765
            {
 
766
              *is_part = 1;
 
767
              *p = '\0';
 
768
            }
 
769
 
 
770
          return path;
 
771
        }
 
772
 
 
773
      /* If this is an AOE disk.  */
 
774
      if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0)
 
775
        {
 
776
          /* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */
 
777
          p = strchr (p, 'p');
 
778
          if (p)
 
779
            {
 
780
              *is_part = 1;
 
781
              *p = '\0';
 
782
            }
 
783
 
 
784
          return path;
 
785
        }
 
786
 
 
787
      /* If this is a Compaq Intelligent Drive Array.  */
 
788
      if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
 
789
        {
 
790
          /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
 
791
          p = strchr (p, 'p');
 
792
          if (p)
 
793
            {
 
794
              *is_part = 1;
 
795
              *p = '\0';
 
796
            }
 
797
 
 
798
          return path;
 
799
        }
 
800
 
 
801
      /* If this is an I2O disk.  */
 
802
      if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
 
803
        {
 
804
          /* /dev/i2o/hd[a-z]([0-9]+)? */
 
805
          if (p[sizeof ("i2o/hda") - 1])
 
806
            *is_part = 1;
 
807
          p[sizeof ("i2o/hda") - 1] = '\0';
 
808
          return path;
 
809
        }
 
810
 
 
811
      /* If this is a MultiMediaCard (MMC).  */
 
812
      if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
 
813
        {
 
814
          /* /dev/mmcblk[0-9]+(p[0-9]+)? */
 
815
          p = strchr (p, 'p');
 
816
          if (p)
 
817
            {
 
818
              *is_part = 1;
 
819
              *p = '\0';
 
820
            }
 
821
 
 
822
          return path;
 
823
        }
 
824
 
 
825
      if (strncmp ("md", p, 2) == 0
 
826
          && p[2] >= '0' && p[2] <= '9')
 
827
        {
 
828
          char *ptr = p + 2;
 
829
          while (*ptr >= '0' && *ptr <= '9')
 
830
            ptr++;
 
831
          if (*ptr)
 
832
            *is_part = 1;
 
833
          *ptr = 0;
 
834
          return path;
 
835
        }
 
836
 
 
837
      if (strncmp ("nbd", p, 3) == 0
 
838
          && p[3] >= '0' && p[3] <= '9')
 
839
        {
 
840
          char *ptr = p + 3;
 
841
          while (*ptr >= '0' && *ptr <= '9')
 
842
            ptr++;
 
843
          if (*ptr)
 
844
            *is_part = 1;
 
845
          *ptr = 0;
 
846
          return path;
 
847
        }
 
848
 
 
849
      /* If this is an IDE, SCSI or Virtio disk.  */
 
850
      if (strncmp ("vdisk", p, 5) == 0
 
851
          && p[5] >= 'a' && p[5] <= 'z')
 
852
        {
 
853
          /* /dev/vdisk[a-z][0-9]* */
 
854
          if (p[6])
 
855
            *is_part = 1;
 
856
          p[6] = '\0';
 
857
          return path;
 
858
        }
 
859
      if ((strncmp ("hd", p, 2) == 0
 
860
           || strncmp ("vd", p, 2) == 0
 
861
           || strncmp ("sd", p, 2) == 0)
 
862
          && p[2] >= 'a' && p[2] <= 'z')
 
863
        {
 
864
          char *pp = p + 2;
 
865
          while (*pp >= 'a' && *pp <= 'z')
 
866
            pp++;
 
867
          if (*pp)
 
868
            *is_part = 1;
 
869
          /* /dev/[hsv]d[a-z]+[0-9]* */
 
870
          *pp = '\0';
 
871
          return path;
 
872
        }
 
873
 
 
874
      /* If this is a Xen virtual block device.  */
 
875
      if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
 
876
        {
 
877
          char *pp = p + 3;
 
878
          while (*pp >= 'a' && *pp <= 'z')
 
879
            pp++;
 
880
          if (*pp)
 
881
            *is_part = 1;
 
882
          /* /dev/xvd[a-z]+[0-9]* */
 
883
          *pp = '\0';
 
884
          return path;
 
885
        }
 
886
 
 
887
      /* If this is a FusionIO disk.  */
 
888
      if ((strncmp ("fio", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
 
889
        {
 
890
          char *pp = p + 3;
 
891
          while (*pp >= 'a' && *pp <= 'z')
 
892
            pp++;
 
893
          if (*pp)
 
894
            *is_part = 1;
 
895
          /* /dev/fio[a-z]+[0-9]* */
 
896
          *pp = '\0';
 
897
          return path;
 
898
        }
 
899
    }
 
900
 
 
901
  return path;
 
902
}
 
903
 
 
904
static char *
 
905
grub_util_get_raid_grub_dev (const char *os_dev)
 
906
{
 
907
  char *grub_dev = NULL;
 
908
  if (os_dev[7] == '_' && os_dev[8] == 'd')
 
909
    {
 
910
      /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
 
911
 
 
912
      char *p, *q;
 
913
 
 
914
      p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
 
915
 
 
916
      q = strchr (p, 'p');
 
917
      if (q)
 
918
        *q = ',';
 
919
 
 
920
      grub_dev = xasprintf ("md%s", p);
 
921
      free (p);
 
922
    }
 
923
  else if (os_dev[7] == '/' && os_dev[8] == 'd')
 
924
    {
 
925
      /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
 
926
 
 
927
      char *p, *q;
 
928
 
 
929
      p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
 
930
 
 
931
      q = strchr (p, 'p');
 
932
      if (q)
 
933
        *q = ',';
 
934
 
 
935
      grub_dev = xasprintf ("md%s", p);
 
936
      free (p);
 
937
    }
 
938
  else if (os_dev[7] >= '0' && os_dev[7] <= '9')
 
939
    {
 
940
      char *p , *q;
 
941
 
 
942
      p = strdup (os_dev + sizeof ("/dev/md") - 1);
 
943
 
 
944
      q = strchr (p, 'p');
 
945
      if (q)
 
946
        *q = ',';
 
947
 
 
948
      grub_dev = xasprintf ("md%s", p);
 
949
      free (p);
 
950
    }
 
951
  else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
 
952
    {
 
953
      char *p , *q;
 
954
 
 
955
      p = strdup (os_dev + sizeof ("/dev/md/") - 1);
 
956
 
 
957
      q = strchr (p, 'p');
 
958
      if (q)
 
959
        *q = ',';
 
960
 
 
961
      grub_dev = xasprintf ("md%s", p);
 
962
      free (p);
 
963
    }
 
964
  else if (os_dev[7] == '/')
 
965
    {
 
966
      /* mdraid 1.x with a free name.  */
 
967
      char *p , *q;
 
968
 
 
969
      p = strdup (os_dev + sizeof ("/dev/md/") - 1);
 
970
 
 
971
      q = strchr (p, 'p');
 
972
      if (q)
 
973
        *q = ',';
 
974
 
 
975
      grub_dev = xasprintf ("md/%s", p);
 
976
      free (p);
 
977
    }
 
978
  else
 
979
    grub_util_error (_("unknown kind of RAID device `%s'"), os_dev);
 
980
 
 
981
  {
 
982
    char *mdadm_name = get_mdadm_uuid (os_dev);
 
983
 
 
984
    if (mdadm_name)
 
985
      {
 
986
        const char *q;
 
987
 
 
988
        for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
 
989
               && grub_isdigit (*q); q--);
 
990
 
 
991
        if (q >= os_dev && *q == 'p')
 
992
          {
 
993
            free (grub_dev);
 
994
            grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
 
995
            goto done;
 
996
          }
 
997
        free (grub_dev);
 
998
        grub_dev = xasprintf ("mduuid/%s", mdadm_name);
 
999
 
 
1000
      done:
 
1001
        free (mdadm_name);
 
1002
      }
 
1003
  }
 
1004
  return grub_dev;
 
1005
}
 
1006
 
 
1007
enum grub_dev_abstraction_types
 
1008
grub_util_get_dev_abstraction_os (const char *os_dev)
 
1009
{
 
1010
#ifndef HAVE_DEVICE_MAPPER
 
1011
  if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
 
1012
    return GRUB_DEV_ABSTRACTION_LVM;
 
1013
#endif
 
1014
 
 
1015
  /* Check for RAID.  */
 
1016
  if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev)
 
1017
      && !grub_util_is_imsm (os_dev))
 
1018
    return GRUB_DEV_ABSTRACTION_RAID;
 
1019
  return GRUB_DEV_ABSTRACTION_NONE;
 
1020
}
 
1021
 
 
1022
int
 
1023
grub_util_pull_device_os (const char *os_dev,
 
1024
                          enum grub_dev_abstraction_types ab)
 
1025
{
 
1026
  switch (ab)
 
1027
    {
 
1028
    case GRUB_DEV_ABSTRACTION_RAID:
 
1029
      {
 
1030
        char **devicelist = grub_util_raid_getmembers (os_dev, 0);
 
1031
        int i;
 
1032
        for (i = 0; devicelist[i];i++)
 
1033
          {
 
1034
            grub_util_pull_device (devicelist[i]);
 
1035
            free (devicelist[i]);
 
1036
          }
 
1037
        free (devicelist);
 
1038
      }
 
1039
      return 1;
 
1040
    default:
 
1041
      return 0;
 
1042
    }
 
1043
}
 
1044
 
 
1045
char *
 
1046
grub_util_get_grub_dev_os (const char *os_dev)
 
1047
{
 
1048
  char *grub_dev = NULL;
 
1049
 
 
1050
  switch (grub_util_get_dev_abstraction (os_dev))
 
1051
    {
 
1052
      /* Fallback for non-devmapper build. In devmapper-builds LVM is handled
 
1053
         in rub_util_get_devmapper_grub_dev and this point isn't reached.
 
1054
       */
 
1055
    case GRUB_DEV_ABSTRACTION_LVM:
 
1056
      {
 
1057
        unsigned short len;
 
1058
        grub_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1;
 
1059
 
 
1060
        len = strlen (os_dev) - offset + 1;
 
1061
        grub_dev = xmalloc (len + sizeof ("lvm/"));
 
1062
 
 
1063
        grub_memcpy (grub_dev, "lvm/", sizeof ("lvm/") - 1);
 
1064
        grub_memcpy (grub_dev + sizeof ("lvm/") - 1, os_dev + offset, len);
 
1065
      }
 
1066
      break;
 
1067
 
 
1068
    case GRUB_DEV_ABSTRACTION_RAID:
 
1069
      grub_dev = grub_util_get_raid_grub_dev (os_dev);
 
1070
      break;
 
1071
 
 
1072
    default:  /* GRUB_DEV_ABSTRACTION_NONE */
 
1073
      break;
 
1074
    }
 
1075
 
 
1076
  return grub_dev;
 
1077
}
 
1078
 
 
1079
char *
 
1080
grub_make_system_path_relative_to_its_root_os (const char *path)
 
1081
{
 
1082
  char *bind = NULL;
 
1083
  grub_size_t len;
 
1084
  grub_free (grub_find_root_devices_from_mountinfo (path, &bind));
 
1085
  if (bind && bind[0])
 
1086
    {
 
1087
      len = strlen (bind);
 
1088
      while (len > 0 && bind[len - 1] == '/')
 
1089
        {
 
1090
          bind[len - 1] = '\0';
 
1091
          len--;
 
1092
        }
 
1093
      return bind;
 
1094
    }
 
1095
  grub_free (bind);
 
1096
  return NULL;
 
1097
}