vaseboot/VasEBoot-core/osdep/linux/getroot.c

1152 lines
26 KiB
C

/*
* VasEBoot -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc.
*
* VasEBoot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VasEBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VasEBoot. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config-util.h>
#include <config.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
#include <VasEBoot/types.h>
#include <sys/ioctl.h> /* ioctl */
#include <sys/mount.h>
#include <VasEBoot/util/misc.h>
#include <VasEBoot/mm.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/emu/misc.h>
#include <VasEBoot/emu/hostdisk.h>
#include <VasEBoot/emu/getroot.h>
#include <sys/wait.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/raid/md_p.h>
#include <linux/raid/md_u.h>
#include <VasEBoot/i18n.h>
#include <VasEBoot/emu/exec.h>
#include <VasEBoot/btrfs.h>
#define LVM_DEV_MAPPER_STRING "/dev/mapper/"
/* Defines taken from btrfs/ioctl.h. */
struct btrfs_ioctl_dev_info_args
{
VasEBoot_uint64_t devid;
VasEBoot_uint8_t uuid[16];
VasEBoot_uint64_t bytes_used;
VasEBoot_uint64_t total_bytes;
VasEBoot_uint64_t unused[379];
VasEBoot_uint8_t path[1024];
};
struct btrfs_ioctl_fs_info_args
{
VasEBoot_uint64_t max_id;
VasEBoot_uint64_t num_devices;
VasEBoot_uint8_t fsid[16];
VasEBoot_uint64_t reserved[124];
};
struct btrfs_ioctl_ino_lookup_args
{
VasEBoot_uint64_t treeid;
VasEBoot_uint64_t objectid;
char name[4080];
};
struct btrfs_ioctl_search_key
{
VasEBoot_uint64_t tree_id;
VasEBoot_uint64_t min_objectid;
VasEBoot_uint64_t max_objectid;
VasEBoot_uint64_t min_offset;
VasEBoot_uint64_t max_offset;
VasEBoot_uint64_t min_transid;
VasEBoot_uint64_t max_transid;
VasEBoot_uint32_t min_type;
VasEBoot_uint32_t max_type;
VasEBoot_uint32_t nr_items;
VasEBoot_uint32_t unused[9];
};
struct btrfs_ioctl_search_args {
struct btrfs_ioctl_search_key key;
VasEBoot_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key))
/ sizeof (VasEBoot_uint64_t)];
};
#define BTRFS_IOC_TREE_SEARCH _IOWR(0x94, 17, \
struct btrfs_ioctl_search_args)
#define BTRFS_IOC_INO_LOOKUP _IOWR(0x94, 18, \
struct btrfs_ioctl_ino_lookup_args)
#define BTRFS_IOC_DEV_INFO _IOWR(0x94, 30, \
struct btrfs_ioctl_dev_info_args)
#define BTRFS_IOC_FS_INFO _IOR(0x94, 31, \
struct btrfs_ioctl_fs_info_args)
static int
VasEBoot_util_is_imsm (const char *os_dev);
#define ESCAPED_PATH_MAX (4 * PATH_MAX)
struct mountinfo_entry
{
int id;
int major, minor;
char enc_root[ESCAPED_PATH_MAX + 1], enc_path[ESCAPED_PATH_MAX + 1];
char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
};
static char **
VasEBoot_util_raid_getmembers (const char *name, int bootable)
{
int fd, ret, i, j;
char **devicelist;
mdu_version_t version;
mdu_array_info_t info;
mdu_disk_info_t disk;
fd = open (name, O_RDONLY);
if (fd == -1)
VasEBoot_util_error (_("cannot open `%s': %s"), name, strerror (errno));
ret = ioctl (fd, RAID_VERSION, &version);
if (ret != 0)
VasEBoot_util_error (_("ioctl RAID_VERSION error: %s"), strerror (errno));
if ((version.major != 0 || version.minor != 90)
&& (version.major != 1 || version.minor != 0)
&& (version.major != 1 || version.minor != 1)
&& (version.major != 1 || version.minor != 2))
VasEBoot_util_error (_("unsupported RAID version: %d.%d"),
version.major, version.minor);
if (bootable && (version.major != 0 || version.minor != 90))
VasEBoot_util_error (_("unsupported RAID version: %d.%d"),
version.major, version.minor);
ret = ioctl (fd, GET_ARRAY_INFO, &info);
if (ret != 0)
VasEBoot_util_error (_("ioctl GET_ARRAY_INFO error: %s"), strerror (errno));
devicelist = xmalloc ((info.nr_disks + 1) * sizeof (char *));
for (i = 0, j = 0; j < info.nr_disks; i++)
{
disk.number = i;
ret = ioctl (fd, GET_DISK_INFO, &disk);
if (ret != 0)
VasEBoot_util_error (_("ioctl GET_DISK_INFO error: %s"), strerror (errno));
if (disk.state & (1 << MD_DISK_REMOVED))
continue;
if (disk.state & (1 << MD_DISK_ACTIVE))
devicelist[j] = VasEBoot_find_device (NULL,
makedev (disk.major, disk.minor));
else
devicelist[j] = NULL;
j++;
}
devicelist[j] = NULL;
close (fd);
return devicelist;
}
/* Statting something on a btrfs filesystem always returns a virtual device
major/minor pair rather than the real underlying device, because btrfs
can span multiple underlying devices (and even if it's currently only
using a single device it can be dynamically extended onto another). We
can't deal with the multiple-device case yet, but in the meantime, we can
at least cope with the single-device case by scanning
/proc/self/mountinfo. */
static void
unescape (char *str)
{
char *optr;
const char *iptr;
for (iptr = optr = str; *iptr; optr++)
{
if (iptr[0] == '\\' && iptr[1] >= '0' && iptr[1] < '8'
&& iptr[2] >= '0' && iptr[2] < '8'
&& iptr[3] >= '0' && iptr[3] < '8')
{
*optr = (((iptr[1] - '0') << 6) | ((iptr[2] - '0') << 3)
| (iptr[3] - '0'));
iptr += 4;
}
else
*optr = *iptr++;
}
*optr = 0;
}
static char **
VasEBoot_find_root_devices_from_btrfs (const char *dir)
{
int fd;
struct btrfs_ioctl_fs_info_args fsi;
int i, j = 0;
char **ret;
fd = open (dir, 0);
if (fd < 0)
return NULL;
if (ioctl (fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
{
close (fd);
return NULL;
}
ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
{
struct btrfs_ioctl_dev_info_args devi;
memset (&devi, 0, sizeof (devi));
devi.devid = i;
if (ioctl (fd, BTRFS_IOC_DEV_INFO, &devi) < 0)
{
close (fd);
free (ret);
return NULL;
}
ret[j++] = xstrdup ((char *) devi.path);
if (j >= fsi.num_devices)
break;
}
close (fd);
ret[j] = 0;
return ret;
}
static char *
get_btrfs_fs_prefix (const char *mount_path)
{
struct btrfs_ioctl_ino_lookup_args args;
struct stat st;
int fd;
VasEBoot_uint64_t tree_id, inode_id;
char *ret = NULL;
fd = open (mount_path, O_RDONLY);
if (fd < 0)
return NULL;
memset (&args, 0, sizeof(args));
args.objectid = VasEBoot_BTRFS_TREE_ROOT_OBJECTID;
if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0)
goto fail;
tree_id = args.treeid;
if (fstat (fd, &st) < 0)
goto fail;
inode_id = st.st_ino;
while (tree_id != VasEBoot_BTRFS_ROOT_VOL_OBJECTID
|| inode_id != VasEBoot_BTRFS_TREE_ROOT_OBJECTID)
{
const char *name;
size_t namelen;
struct btrfs_ioctl_search_args sargs;
char *old;
memset (&sargs, 0, sizeof(sargs));
if (inode_id == VasEBoot_BTRFS_TREE_ROOT_OBJECTID)
{
struct VasEBoot_btrfs_root_backref *br;
sargs.key.tree_id = 1;
sargs.key.min_objectid = tree_id;
sargs.key.max_objectid = tree_id;
sargs.key.min_offset = 0;
sargs.key.max_offset = ~0ULL;
sargs.key.min_transid = 0;
sargs.key.max_transid = ~0ULL;
sargs.key.min_type = VasEBoot_BTRFS_ITEM_TYPE_ROOT_BACKREF;
sargs.key.max_type = VasEBoot_BTRFS_ITEM_TYPE_ROOT_BACKREF;
sargs.key.nr_items = 1;
if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
goto fail;
if (sargs.key.nr_items == 0)
goto fail;
tree_id = sargs.buf[2];
br = (struct VasEBoot_btrfs_root_backref *) (sargs.buf + 4);
inode_id = VasEBoot_le_to_cpu64 (br->inode_id);
name = br->name;
namelen = VasEBoot_le_to_cpu16 (br->n);
}
else
{
struct VasEBoot_btrfs_inode_ref *ir;
sargs.key.tree_id = tree_id;
sargs.key.min_objectid = inode_id;
sargs.key.max_objectid = inode_id;
sargs.key.min_offset = 0;
sargs.key.max_offset = ~0ULL;
sargs.key.min_transid = 0;
sargs.key.max_transid = ~0ULL;
sargs.key.min_type = VasEBoot_BTRFS_ITEM_TYPE_INODE_REF;
sargs.key.max_type = VasEBoot_BTRFS_ITEM_TYPE_INODE_REF;
if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0)
goto fail;
if (sargs.key.nr_items == 0)
goto fail;
inode_id = sargs.buf[2];
ir = (struct VasEBoot_btrfs_inode_ref *) (sargs.buf + 4);
name = ir->name;
namelen = VasEBoot_le_to_cpu16 (ir->n);
}
old = ret;
ret = xmalloc (namelen + (old ? strlen (old) : 0) + 2);
ret[0] = '/';
memcpy (ret + 1, name, namelen);
if (old)
{
strcpy (ret + 1 + namelen, old);
free (old);
}
else
ret[1+namelen] = '\0';
}
if (!ret)
ret = xstrdup ("/");
close (fd);
return ret;
fail:
free (ret);
close (fd);
return NULL;
}
char **
VasEBoot_find_root_devices_from_mountinfo (const char *dir, char **relroot)
{
FILE *fp = NULL;
char *buf = NULL;
size_t len = 0;
VasEBoot_size_t entry_len, entry_max = 4;
struct mountinfo_entry *entries;
struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
int i;
int retry = 0;
int dir_fd = -1;
char **ret = NULL;
if (! *dir)
dir = "/";
if (relroot)
*relroot = NULL;
entries = xmalloc (entry_max * sizeof (*entries));
again:
fp = VasEBoot_util_fopen ("/proc/self/mountinfo", "r");
if (! fp)
goto out; /* fall through to other methods */
entry_len = 0;
/* First, build a list of relevant visible mounts. */
while (getline (&buf, &len, fp) > 0)
{
struct mountinfo_entry entry;
int count;
size_t enc_path_len;
const char *sep;
if (sscanf (buf, "%d %d %u:%u %s %s%n",
&entry.id, &parent_entry.id, &entry.major, &entry.minor,
entry.enc_root, entry.enc_path, &count) < 6)
continue;
unescape (entry.enc_root);
unescape (entry.enc_path);
enc_path_len = strlen (entry.enc_path);
/* Check that enc_path is a prefix of dir. The prefix must either be
the entire string, or end with a slash, or be immediately followed
by a slash. */
if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
(enc_path_len && dir[enc_path_len - 1] != '/' &&
dir[enc_path_len] && dir[enc_path_len] != '/'))
continue;
sep = strstr (buf + count, " - ");
if (!sep)
continue;
sep += sizeof (" - ") - 1;
if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
continue;
unescape (entry.device);
/* Using the mount IDs, find out where this fits in the list of
visible mount entries we've seen so far. There are three
interesting cases. Firstly, it may be inserted at the end: this is
the usual case of /foo/bar being mounted after /foo. Secondly, it
may be inserted at the start: for example, this can happen for
filesystems that are mounted before / and later moved under it.
Thirdly, it may occlude part or all of the existing filesystem
tree, in which case the end of the list needs to be pruned and this
new entry will be inserted at the end. */
if (entry_len >= entry_max)
{
entry_max <<= 1;
entries = xrealloc (entries, entry_max * sizeof (*entries));
}
if (!entry_len)
{
/* Initialise list. */
entry_len = 2;
entries[0] = parent_entry;
entries[1] = entry;
}
else
{
for (i = entry_len - 1; i >= 0; i--)
{
if (entries[i].id == parent_entry.id)
{
/* Insert at end, pruning anything previously above this. */
entry_len = i + 2;
entries[i + 1] = entry;
break;
}
else if (i == 0 && entries[i].id == entry.id)
{
/* Insert at start. */
entry_len++;
memmove (entries + 1, entries,
(entry_len - 1) * sizeof (*entries));
entries[0] = parent_entry;
entries[1] = entry;
break;
}
}
}
}
/* Now scan visible mounts for the ones we're interested in. */
for (i = entry_len - 1; i >= 0; i--)
{
char *fs_prefix = NULL;
if (!*entries[i].device)
continue;
if (VasEBoot_strcmp (entries[i].fstype, "fuse.zfs") == 0
|| VasEBoot_strcmp (entries[i].fstype, "zfs") == 0)
{
char *slash;
slash = strchr (entries[i].device, '/');
if (slash)
*slash = 0;
ret = VasEBoot_util_find_root_devices_from_poolname (entries[i].device);
if (slash)
*slash = '/';
if (relroot)
{
if (!slash)
fs_prefix = xasprintf ("/@%s", entries[i].enc_root);
else if (strchr (slash + 1, '@'))
fs_prefix = xasprintf ("/%s%s", slash + 1, entries[i].enc_root);
else
fs_prefix = xasprintf ("/%s@%s", slash + 1,
entries[i].enc_root);
}
}
else if (VasEBoot_strcmp (entries[i].fstype, "btrfs") == 0)
{
ret = VasEBoot_find_root_devices_from_btrfs (dir);
fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path);
}
else if (!retry && VasEBoot_strcmp (entries[i].fstype, "autofs") == 0)
{
/* If the best match is automounted, try to trigger mount. We cannot
simply return here because stat() on automounted directory does not
trigger mount and returns bogus (pseudo)device number instead.
We keep mountpoint open until end of scan to prevent timeout. */
int flags = O_RDONLY|O_DIRECTORY;
fclose (fp);
#ifdef O_LARGEFILE
flags |= O_LARGEFILE;
#endif
dir_fd = open (entries[i].enc_path, flags);
retry = 1;
goto again;
}
if (!ret)
{
ret = xmalloc (2 * sizeof (ret[0]));
ret[0] = strdup (entries[i].device);
ret[1] = 0;
}
if (!fs_prefix)
fs_prefix = entries[i].enc_root;
if (relroot)
{
char *ptr;
VasEBoot_size_t enc_root_len = strlen (fs_prefix);
VasEBoot_size_t enc_path_len = strlen (entries[i].enc_path);
VasEBoot_size_t dir_strlen = strlen (dir);
*relroot = xmalloc (enc_root_len +
2 + dir_strlen);
ptr = VasEBoot_stpcpy (*relroot, fs_prefix);
if (dir_strlen > enc_path_len)
{
while (ptr > *relroot && *(ptr - 1) == '/')
ptr--;
if (dir[enc_path_len] != '/')
*ptr++ = '/';
ptr = VasEBoot_stpcpy (ptr, dir + enc_path_len);
}
*ptr = 0;
}
if (fs_prefix != entries[i].enc_root)
free (fs_prefix);
break;
}
out:
free (buf);
free (entries);
if (fp)
fclose (fp);
if (dir_fd != -1)
close (dir_fd);
return ret;
}
static char *
get_mdadm_uuid (const char *os_dev)
{
const char *argv[5];
int fd;
pid_t pid;
FILE *mdadm;
char *buf = NULL;
size_t len = 0;
char *name = NULL;
argv[0] = "mdadm";
argv[1] = "--detail";
argv[2] = "--export";
argv[3] = os_dev;
argv[4] = NULL;
pid = VasEBoot_util_exec_pipe (argv, &fd);
if (!pid)
return NULL;
/* Parent. Read mdadm's output. */
mdadm = fdopen (fd, "r");
if (! mdadm)
{
VasEBoot_util_warn (_("Unable to open stream from %s: %s"),
"mdadm", strerror (errno));
goto out;
}
while (getline (&buf, &len, mdadm) > 0)
{
if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
{
char *name_start, *ptri, *ptro;
free (name);
name_start = buf + sizeof ("MD_UUID=") - 1;
ptro = name = xmalloc (strlen (name_start) + 1);
for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
ptri++)
if ((*ptri >= '0' && *ptri <= '9')
|| (*ptri >= 'a' && *ptri <= 'f')
|| (*ptri >= 'A' && *ptri <= 'F'))
*ptro++ = *ptri;
*ptro = 0;
}
}
out:
close (fd);
waitpid (pid, NULL, 0);
free (buf);
return name;
}
static int
VasEBoot_util_is_imsm (const char *os_dev)
{
int retry;
int is_imsm = 0;
int container_seen = 0;
const char *dev = os_dev;
do
{
const char *argv[5];
int fd;
pid_t pid;
FILE *mdadm;
char *buf = NULL;
size_t len = 0;
retry = 0; /* We'll do one more pass if device is part of container */
argv[0] = "mdadm";
argv[1] = "--detail";
argv[2] = "--export";
argv[3] = dev;
argv[4] = NULL;
pid = VasEBoot_util_exec_pipe (argv, &fd);
if (!pid)
{
if (dev != os_dev)
free ((void *) dev);
return 0;
}
/* Parent. Read mdadm's output. */
mdadm = fdopen (fd, "r");
if (! mdadm)
{
VasEBoot_util_warn (_("Unable to open stream from %s: %s"),
"mdadm", strerror (errno));
close (fd);
waitpid (pid, NULL, 0);
if (dev != os_dev)
free ((void *) dev);
return 0;
}
while (getline (&buf, &len, mdadm) > 0)
{
if (strncmp (buf, "MD_CONTAINER=", sizeof ("MD_CONTAINER=") - 1) == 0
&& !container_seen)
{
char *newdev, *ptr;
newdev = xstrdup (buf + sizeof ("MD_CONTAINER=") - 1);
ptr = newdev + strlen (newdev) - 1;
for (; ptr >= newdev && (*ptr == '\n' || *ptr == '\r'); ptr--);
ptr[1] = 0;
VasEBoot_util_info ("Container of %s is %s", dev, newdev);
dev = newdev;
container_seen = retry = 1;
break;
}
if (strncmp (buf, "MD_METADATA=imsm",
sizeof ("MD_METADATA=imsm") - 1) == 0)
{
is_imsm = 1;
VasEBoot_util_info ("%s is imsm", dev);
break;
}
}
free (buf);
close (fd);
waitpid (pid, NULL, 0);
}
while (retry);
if (dev != os_dev)
free ((void *) dev);
return is_imsm;
}
char *
VasEBoot_util_part_to_disk (const char *os_dev, struct stat *st,
int *is_part)
{
char *path;
if (! S_ISBLK (st->st_mode))
{
*is_part = 0;
return xstrdup (os_dev);
}
path = xmalloc (PATH_MAX);
if (! realpath (os_dev, path))
return NULL;
if (strncmp ("/dev/", path, 5) == 0)
{
char *p = path + 5;
/* If this is an IDE disk. */
if (strncmp ("ide/", p, 4) == 0)
{
p = strstr (p, "part");
if (p)
{
*is_part = 1;
strcpy (p, "disc");
}
return path;
}
/* If this is a SCSI disk. */
if (strncmp ("scsi/", p, 5) == 0)
{
p = strstr (p, "part");
if (p)
{
*is_part = 1;
strcpy (p, "disc");
}
return path;
}
/* If this is a DAC960 disk. */
if (strncmp ("rd/c", p, 4) == 0)
{
/* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
/* If this is a Mylex AcceleRAID Array. */
if (strncmp ("rs/c", p, 4) == 0)
{
/* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
/* If this is a CCISS disk. */
if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
{
/* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
/* If this is an AOE disk. */
if (strncmp ("etherd/e", p, sizeof ("etherd/e") - 1) == 0)
{
/* /dev/etherd/e[0-9]+\.[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
/* If this is a Compaq Intelligent Drive Array. */
if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
{
/* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
/* If this is an I2O disk. */
if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
{
/* /dev/i2o/hd[a-z]([0-9]+)? */
if (p[sizeof ("i2o/hda") - 1])
*is_part = 1;
p[sizeof ("i2o/hda") - 1] = '\0';
return path;
}
/* If this is a MultiMediaCard (MMC). */
if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
{
/* /dev/mmcblk[0-9]+(p[0-9]+)? */
p = strchr (p, 'p');
if (p)
{
*is_part = 1;
*p = '\0';
}
return path;
}
if (strncmp ("md", p, 2) == 0
&& p[2] >= '0' && p[2] <= '9')
{
char *ptr = p + 2;
while (*ptr >= '0' && *ptr <= '9')
ptr++;
if (*ptr)
*is_part = 1;
*ptr = 0;
return path;
}
if (strncmp ("nbd", p, 3) == 0
&& p[3] >= '0' && p[3] <= '9')
{
char *ptr = p + 3;
while (*ptr >= '0' && *ptr <= '9')
ptr++;
if (*ptr)
*is_part = 1;
*ptr = 0;
return path;
}
/* If this is an IDE, SCSI or Virtio disk. */
if (strncmp ("vdisk", p, 5) == 0
&& p[5] >= 'a' && p[5] <= 'z')
{
/* /dev/vdisk[a-z][0-9]* */
if (p[6])
*is_part = 1;
p[6] = '\0';
return path;
}
if ((strncmp ("hd", p, 2) == 0
|| strncmp ("vd", p, 2) == 0
|| strncmp ("sd", p, 2) == 0)
&& p[2] >= 'a' && p[2] <= 'z')
{
char *pp = p + 2;
while (*pp >= 'a' && *pp <= 'z')
pp++;
if (*pp)
*is_part = 1;
/* /dev/[hsv]d[a-z]+[0-9]* */
*pp = '\0';
return path;
}
/* If this is a Xen virtual block device. */
if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
{
char *pp = p + 3;
while (*pp >= 'a' && *pp <= 'z')
pp++;
if (*pp)
*is_part = 1;
/* /dev/xvd[a-z]+[0-9]* */
*pp = '\0';
return path;
}
/* If this is a loop device */
if ((strncmp ("loop", p, 4) == 0) && p[4] >= '0' && p[4] <= '9')
{
char *pp = p + 4;
while (*pp >= '0' && *pp <= '9')
pp++;
if (*pp == 'p')
*is_part = 1;
/* /dev/loop[0-9]+p[0-9]* */
*pp = '\0';
return path;
}
/* If this is a NVMe device */
if ((strncmp ("nvme", p, 4) == 0) && p[4] >= '0' && p[4] <= '9')
{
char *pp = p + 4;
while (*pp >= '0' && *pp <= '9')
pp++;
if (*pp == 'n')
pp++;
while (*pp >= '0' && *pp <= '9')
pp++;
if (*pp == 'p')
*is_part = 1;
/* /dev/nvme[0-9]+n[0-9]+p[0-9]* */
*pp = '\0';
return path;
}
}
return path;
}
static char *
VasEBoot_util_get_raid_VasEBoot_dev (const char *os_dev)
{
char *VasEBoot_dev = NULL;
if (os_dev[7] == '_' && os_dev[8] == 'd')
{
/* This a partitionable RAID device of the form /dev/md_dNNpMM. */
char *p, *q;
p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
q = strchr (p, 'p');
if (q)
*q = ',';
VasEBoot_dev = xasprintf ("md%s", p);
free (p);
}
else if (os_dev[7] == '/' && os_dev[8] == 'd')
{
/* This a partitionable RAID device of the form /dev/md/dNNpMM. */
char *p, *q;
p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
q = strchr (p, 'p');
if (q)
*q = ',';
VasEBoot_dev = xasprintf ("md%s", p);
free (p);
}
else if (os_dev[7] >= '0' && os_dev[7] <= '9')
{
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md") - 1);
q = strchr (p, 'p');
if (q)
*q = ',';
VasEBoot_dev = xasprintf ("md%s", p);
free (p);
}
else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
{
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md/") - 1);
q = strchr (p, 'p');
if (q)
*q = ',';
VasEBoot_dev = xasprintf ("md%s", p);
free (p);
}
else if (os_dev[7] == '/')
{
/* mdraid 1.x with a free name. */
char *p , *q;
p = strdup (os_dev + sizeof ("/dev/md/") - 1);
q = strchr (p, 'p');
if (q)
*q = ',';
VasEBoot_dev = xasprintf ("md/%s", p);
free (p);
}
else
VasEBoot_util_error (_("unknown kind of RAID device `%s'"), os_dev);
{
char *mdadm_name = get_mdadm_uuid (os_dev);
if (mdadm_name)
{
const char *q;
for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
&& VasEBoot_isdigit (*q); q--);
if (q >= os_dev && *q == 'p')
{
free (VasEBoot_dev);
VasEBoot_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
goto done;
}
free (VasEBoot_dev);
VasEBoot_dev = xasprintf ("mduuid/%s", mdadm_name);
done:
free (mdadm_name);
}
}
return VasEBoot_dev;
}
enum VasEBoot_dev_abstraction_types
VasEBoot_util_get_dev_abstraction_os (const char *os_dev)
{
#ifndef HAVE_DEVICE_MAPPER
if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
return VasEBoot_DEV_ABSTRACTION_LVM;
#endif
/* Check for RAID. */
if (!strncmp (os_dev, "/dev/md", 7) && ! VasEBoot_util_device_is_mapped (os_dev)
&& !VasEBoot_util_is_imsm (os_dev))
return VasEBoot_DEV_ABSTRACTION_RAID;
return VasEBoot_DEV_ABSTRACTION_NONE;
}
int
VasEBoot_util_pull_device_os (const char *os_dev,
enum VasEBoot_dev_abstraction_types ab)
{
switch (ab)
{
case VasEBoot_DEV_ABSTRACTION_RAID:
{
char **devicelist = VasEBoot_util_raid_getmembers (os_dev, 0);
int i;
for (i = 0; devicelist[i];i++)
{
VasEBoot_util_pull_device (devicelist[i]);
free (devicelist[i]);
}
free (devicelist);
}
return 1;
default:
return 0;
}
}
char *
VasEBoot_util_get_VasEBoot_dev_os (const char *os_dev)
{
char *VasEBoot_dev = NULL;
switch (VasEBoot_util_get_dev_abstraction (os_dev))
{
/* Fallback for non-devmapper build. In devmapper-builds LVM is handled
in VasEBoot_util_get_devmapper_VasEBoot_dev and this point isn't reached.
*/
case VasEBoot_DEV_ABSTRACTION_LVM:
{
unsigned short len;
VasEBoot_size_t offset = sizeof (LVM_DEV_MAPPER_STRING) - 1;
len = strlen (os_dev) - offset + 1;
VasEBoot_dev = xmalloc (len + sizeof ("lvm/"));
VasEBoot_memcpy (VasEBoot_dev, "lvm/", sizeof ("lvm/") - 1);
VasEBoot_memcpy (VasEBoot_dev + sizeof ("lvm/") - 1, os_dev + offset, len);
}
break;
case VasEBoot_DEV_ABSTRACTION_RAID:
VasEBoot_dev = VasEBoot_util_get_raid_VasEBoot_dev (os_dev);
break;
default: /* VasEBoot_DEV_ABSTRACTION_NONE */
break;
}
return VasEBoot_dev;
}
char *
VasEBoot_make_system_path_relative_to_its_root_os (const char *path)
{
char *bind = NULL;
VasEBoot_size_t len;
VasEBoot_free (VasEBoot_find_root_devices_from_mountinfo (path, &bind));
if (bind && bind[0])
{
len = strlen (bind);
while (len > 0 && bind[len - 1] == '/')
{
bind[len - 1] = '\0';
len--;
}
return bind;
}
VasEBoot_free (bind);
return NULL;
}