[syslinux:master] extlinux: better methods for finding device matches

syslinux-bot for H. Peter Anvin hpa at linux.intel.com
Tue Jun 19 17:45:08 PDT 2012


Commit-ID:  0b49326e70074030a9d416d36264fe859cc16328
Gitweb:     http://www.syslinux.org/commit/0b49326e70074030a9d416d36264fe859cc16328
Author:     H. Peter Anvin <hpa at linux.intel.com>
AuthorDate: Tue, 19 Jun 2012 17:41:15 -0700
Committer:  H. Peter Anvin <hpa at linux.intel.com>
CommitDate: Tue, 19 Jun 2012 17:41:15 -0700

extlinux: better methods for finding device matches

1. Support parsing /proc/self/mountinfo for devices;
2. For btrfs, query the device names from btrfs itself.

Signed-off-by: H. Peter Anvin <hpa at linux.intel.com>

---
 extlinux/Makefile                         |    1 +
 extlinux/btrfs.h                          |   76 +++++++-
 extlinux/main.c                           |  129 ++++++++++----
 extlinux/mountinfo.c                      |  277 +++++++++++++++++++++++++++++
 com32/menu/menu.c => extlinux/mountinfo.h |   49 ++---
 5 files changed, 459 insertions(+), 73 deletions(-)

diff --git a/extlinux/Makefile b/extlinux/Makefile
index 865c7a6..6cde574 100644
--- a/extlinux/Makefile
+++ b/extlinux/Makefile
@@ -25,6 +25,7 @@ CFLAGS	 = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \
 LDFLAGS	 = 
 
 SRCS     = main.c \
+	   mountinfo.c \
 	   ../libinstaller/syslxmod.c \
 	   ../libinstaller/syslxopt.c \
 	   ../libinstaller/syslxcom.c \
diff --git a/extlinux/btrfs.h b/extlinux/btrfs.h
index be0c24e..4e2cb31 100644
--- a/extlinux/btrfs.h
+++ b/extlinux/btrfs.h
@@ -8,8 +8,10 @@
 #define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
 #define BTRFS_SUPER_INFO_SIZE 4096
 #define BTRFS_MAGIC "_BHRfS_M"
+#define BTRFS_MAGIC_L 8
 #define BTRFS_CSUM_SIZE 32
 #define BTRFS_FSID_SIZE 16
+#define BTRFS_UUID_SIZE 16
 
 typedef __u64 u64;
 typedef __u32 u32;
@@ -46,17 +48,52 @@ struct btrfs_dir_item {
 } __attribute__ ((__packed__));
 
 struct btrfs_super_block {
-	unsigned char csum[BTRFS_CSUM_SIZE];
-	/* the first 3 fields must match struct btrfs_header */
-	unsigned char fsid[BTRFS_FSID_SIZE];    /* FS specific uuid */
-	u64 bytenr; /* this block number */
-	u64 flags;
-
-	/* allowed to be different from the btrfs_header from here own down */
-	u64 magic;
+        uint8_t csum[32];
+        uint8_t fsid[16];
+        uint64_t bytenr;
+        uint64_t flags;
+        uint8_t magic[8];
+        uint64_t generation;
+        uint64_t root;
+        uint64_t chunk_root;
+        uint64_t log_root;
+        uint64_t log_root_transid;
+        uint64_t total_bytes;
+        uint64_t bytes_used;
+        uint64_t root_dir_objectid;
+        uint64_t num_devices;
+        uint32_t sectorsize;
+        uint32_t nodesize;
+        uint32_t leafsize;
+        uint32_t stripesize;
+        uint32_t sys_chunk_array_size;
+        uint64_t chunk_root_generation;
+        uint64_t compat_flags;
+        uint64_t compat_ro_flags;
+        uint64_t incompat_flags;
+        uint16_t csum_type;
+        uint8_t root_level;
+        uint8_t chunk_root_level;
+        uint8_t log_root_level;
+        struct btrfs_dev_item {
+                uint64_t devid;
+                uint64_t total_bytes;
+                uint64_t bytes_used;
+                uint32_t io_align;
+                uint32_t io_width;
+                uint32_t sector_size;
+                uint64_t type;
+                uint64_t generation;
+                uint64_t start_offset;
+                uint32_t dev_group;
+                uint8_t seek_speed;
+                uint8_t bandwidth;
+                uint8_t uuid[16];
+                uint8_t fsid[16];
+        } __attribute__ ((__packed__)) dev_item;
+        uint8_t label[256];
 } __attribute__ ((__packed__));
 
-
 #define BTRFS_IOCTL_MAGIC 0x94
 #define BTRFS_VOL_NAME_MAX 255
 #define BTRFS_PATH_NAME_MAX 4087
@@ -110,6 +147,23 @@ struct btrfs_ioctl_search_header {
 	__u32 len;
 } __attribute__((may_alias));
 
+#define BTRFS_DEVICE_PATH_NAME_MAX 1024
+struct btrfs_ioctl_dev_info_args {
+	__u64 devid;				/* in/out */
+	__u8 uuid[BTRFS_UUID_SIZE];		/* in/out */
+	__u64 bytes_used;			/* out */
+	__u64 total_bytes;			/* out */
+	__u64 unused[379];			/* pad to 4k */
+	__u8 path[BTRFS_DEVICE_PATH_NAME_MAX];	/* out */
+};
+
+struct btrfs_ioctl_fs_info_args {
+	__u64 max_id;				/* out */
+	__u64 num_devices;			/* out */
+	__u8 fsid[BTRFS_FSID_SIZE];		/* out */
+	__u64 reserved[124];			/* pad to 1k */
+};
+
 #define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
 /*
  * the buf is an array of search headers where
@@ -123,5 +177,9 @@ struct btrfs_ioctl_search_args {
 
 #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
                                    struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \
+				 struct btrfs_ioctl_dev_info_args)
+#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \
+			       struct btrfs_ioctl_fs_info_args)
 
 #endif
diff --git a/extlinux/main.c b/extlinux/main.c
index 5da89e2..15ea037 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -52,6 +52,7 @@
 #include "syslxfs.h"
 #include "setadv.h"
 #include "syslxopt.h" /* unified options */
+#include "mountinfo.h"
 
 #ifdef DEBUG
 # define dprintf printf
@@ -342,7 +343,7 @@ int install_bootblock(int fd, const char *device)
 		perror("reading superblock");
 		return 1;
 	}
-	if (sb2.magic == *(u64 *)BTRFS_MAGIC)
+	if (!memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
 		ok = true;
     } else if (fs_type == VFAT) {
 	if (xpread(fd, &sb3, sizeof sb3, 0) != sizeof sb3) {
@@ -535,35 +536,6 @@ static int test_issubvolume(char *path)
 }
 
 /*
- * Get file handle for a file or dir
- */
-static int open_file_or_dir(const char *fname)
-{
-        int ret;
-        struct stat st;
-        DIR *dirstream;
-        int fd;
-
-        ret = stat(fname, &st);
-        if (ret < 0) {
-                return -1;
-        }
-        if (S_ISDIR(st.st_mode)) {
-                dirstream = opendir(fname);
-                if (!dirstream) {
-                        return -2;
-                }
-                fd = dirfd(dirstream);
-        } else {
-                fd = open(fname, O_RDWR);
-        }
-        if (fd < 0) {
-                return -3;
-        }
-        return fd;
-}
-
-/*
  * Get the default subvolume of a btrfs filesystem
  *   rootdir: btrfs root dir
  *   subvol:  this function will save the default subvolume name here
@@ -585,7 +557,7 @@ static char * get_default_subvol(char * rootdir, char * subvol)
 
     ret = test_issubvolume(rootdir);
     if (ret == 1) {
-        fd = open_file_or_dir(rootdir);
+        fd = open(rootdir, O_RDONLY);
         if (fd < 0) {
             fprintf(stderr, "ERROR: failed to open %s\n", rootdir);
         }
@@ -921,6 +893,83 @@ err:
     return NULL;
 }
 
+static const char *find_device_mountinfo(const char *path, dev_t dev)
+{
+    const struct mountinfo *m;
+    struct stat st;
+
+    m = find_mount(path, NULL);
+
+    if (m->devpath[0] == '/' && m->dev == dev &&
+	!stat(m->devpath, &st) && S_ISBLK(st.st_mode) && st.st_rdev == dev)
+	return m->devpath;
+    else
+	return NULL;
+}
+
+static const char *find_device_btrfs(const char *path)
+{
+    int fd;
+    struct btrfs_ioctl_fs_info_args fsinfo;
+    static struct btrfs_ioctl_dev_info_args devinfo;
+    struct btrfs_super_block sb2;
+    const char *rv = NULL;
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0)
+	goto err;
+
+    if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsinfo))
+	goto err;
+
+    /* We do not support multi-device btrfs yet */
+    if (fsinfo.num_devices != 1)
+	goto err;
+
+    /* The one device will have the max devid */
+    memset(&devinfo, 0, sizeof devinfo);
+    devinfo.devid = fsinfo.max_id;
+    if (ioctl(fd, BTRFS_IOC_DEV_INFO, &devinfo))
+	goto err;
+    close(fd);
+    fd = -1;
+
+    if (devinfo.path[0] != '/')
+	goto err;
+
+    fd = open((const char *)devinfo.path, O_RDONLY);
+    if (fd < 0)
+	goto err;
+
+    if (xpread(fd, &sb2, sizeof sb2, BTRFS_SUPER_INFO_OFFSET) != sizeof sb2)
+	goto err;
+
+    if (memcmp(sb2.magic, BTRFS_MAGIC, BTRFS_MAGIC_L))
+	goto err;
+
+    if (memcmp(sb2.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
+	goto err;
+
+    if (sb2.num_devices != 1)
+	goto err;
+
+    if (sb2.dev_item.devid != devinfo.devid)
+	goto err;
+
+    if (memcmp(sb2.dev_item.uuid, devinfo.uuid, sizeof devinfo.uuid))
+	goto err;
+
+    if (memcmp(sb2.dev_item.fsid, fsinfo.fsid, sizeof fsinfo.fsid))
+	goto err;
+
+    rv = (const char *)devinfo.path;		/* It's good! */
+
+err:
+    if (fd >= 0)
+	close(fd);
+    return rv;
+}
+
 static const char *get_devname(const char *path)
 {
     const char *devname = NULL;
@@ -936,10 +985,19 @@ static const char *get_devname(const char *path)
 	return devname;
     }
 
-#ifdef __KLIBC__
+    if (fs_type == BTRFS) {
+	/* For btrfs try to get the device name from btrfs itself */
+	devname = find_device_btrfs(path);
+    }
 
-    devname = find_device_sysfs(st.st_dev);
+    if (!devname) {
+	devname = find_device_mountinfo(path, st.st_dev);
+    }
 
+#ifdef __KLIBC__
+    if (!devname) {
+	devname = find_device_sysfs(st.st_dev);
+    }
     if (!devname) {
 	/* klibc doesn't have getmntent and friends; instead, just create
 	   a new device with the appropriate device type */
@@ -956,8 +1014,9 @@ static const char *get_devname(const char *path)
     }
 
 #else
-
-    devname = find_device("/proc/mounts", st.st_dev);
+    if (!devname) {
+	devname = find_device("/proc/mounts", st.st_dev);
+    }
     if (!devname) {
 	/* Didn't find it in /proc/mounts, try /etc/mtab */
         devname = find_device("/etc/mtab", st.st_dev);
diff --git a/extlinux/mountinfo.c b/extlinux/mountinfo.c
new file mode 100644
index 0000000..2be8758
--- /dev/null
+++ b/extlinux/mountinfo.c
@@ -0,0 +1,277 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2012 Intel Corporation; All Rights Reserved
+ *
+ *   This program 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, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include "mountinfo.h"
+
+/*
+ * Parse /proc/self/mountinfo
+ */
+static int get_string(FILE *f, char *string_buf, size_t string_len, char *ec)
+{
+    int ch;
+    char *p = string_buf;
+
+    for (;;) {
+	if (!string_len)
+	    return -2;		/* String too long */
+
+	ch = getc(f);
+	if (ch == EOF) {
+	    return -1;		/* Got EOF */
+	} else if (ch == ' ' || ch == '\t' || ch == '\n') {
+	    *ec = ch;
+	    *p = '\0';
+	    return p - string_buf;
+	} else if (ch == '\\') {
+	    /* Should always be followed by 3 octal digits in 000..377 */
+	    int oc = 0;
+	    int i;
+	    for (i = 0; i < 3; i++) {
+		ch = getc(f);
+		if (ch < '0' || ch > '7' || (i == 0 && ch > '3'))
+		    return -1;	/* Bad escape sequence */
+		oc = (oc << 3) + (ch - '0');
+	    }
+	    if (!oc)
+		return -1;	/* We can't handle \000 */
+	    *p++ = oc;
+	    string_len--;
+	} else {
+	    *p++ = ch;
+	    string_len--;
+	}
+    }
+}
+
+static void free_mountinfo(struct mountinfo *m)
+{
+    struct mountinfo *nx;
+
+    while (m) {
+	free((char *)m->root);
+	free((char *)m->path);
+	free((char *)m->fstype);
+	free((char *)m->devpath);
+	free((char *)m->mountopt);
+	nx = m->next;
+	free(m);
+	m = nx;
+    }
+}
+
+static struct mountinfo *head = NULL, **tail = &head;
+
+static void parse_mountinfo(void)
+{
+    FILE *f;
+    struct mountinfo *m, *mm;
+    char string_buf[PATH_MAX*8];
+    int n;
+    char ec, *ep;
+    unsigned int ma, mi;
+
+    f = fopen("/proc/self/mountinfo", "r");
+    if (!f)
+	return;
+
+    for (;;) {
+	m = malloc(sizeof(struct mountinfo));
+	if (!m)
+	    break;
+	memset(m, 0, sizeof *m);
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0 || ec == '\n')
+	    break;
+
+	m->mountid = strtoul(string_buf, &ep, 10);
+	if (*ep)
+	    break;
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0 || ec == '\n')
+	    break;
+
+	m->parentid = strtoul(string_buf, &ep, 10);
+	if (*ep)
+	    break;
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0 || ec == '\n')
+	    break;
+
+	if (sscanf(string_buf, "%u:%u", &ma, &mi) != 2)
+	    break;
+
+	m->dev = makedev(ma, mi);
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 1 || ec == '\n' || string_buf[0] != '/')
+	    break;
+
+	m->root = strdup(string_buf);
+	if (!m->root)
+	    break;
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 1 || ec == '\n' || string_buf[0] != '/')
+	    break;
+
+	m->path = strdup(string_buf);
+	m->pathlen = (n == 1) ? 0 : n; /* Treat / as empty */
+
+	/* Skip tagged attributes */
+	do {
+	    n = get_string(f, string_buf, sizeof string_buf, &ec);
+	    if (n < 0 || ec == '\n')
+		goto quit;
+	} while (n != 1 || string_buf[0] != '-');
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0 || ec == '\n')
+	    break;
+
+	m->fstype = strdup(string_buf);
+	if (!m->fstype)
+	    break;
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0 || ec == '\n')
+	    break;
+
+	m->devpath = strdup(string_buf);
+	if (!m->devpath)
+	    break;
+
+	n = get_string(f, string_buf, sizeof string_buf, &ec);
+	if (n < 0)
+	    break;
+
+	m->mountopt = strdup(string_buf);
+	if (!m->mountopt)
+	    break;
+
+	/* Skip any previously unknown fields */
+	while (ec != '\n' && ec != EOF)
+	    ec = getc(f);
+
+	*tail = m;
+	tail = &m->next;
+    }
+quit:
+    fclose(f);
+    free_mountinfo(m);
+
+    /* Create parent links */
+    for (m = head; m; m = m->next) {
+	for (mm = head; mm; mm = mm->next) {
+	    if (m->parentid == mm->mountid) {
+		m->parent = mm;
+		if (!strcmp(m->path, mm->path))
+		    mm->hidden = 1; /* Hidden under another mount */
+		break;
+	    }
+	}
+    }
+}
+
+const struct mountinfo *find_mount(const char *path, char **subpath)
+{
+    static int done_init;
+    char *real_path;
+    const struct mountinfo *m, *best;
+    struct stat st;
+    int len, matchlen;
+
+    if (!done_init) {
+	parse_mountinfo();
+	done_init = 1;
+    }
+
+    if (stat(path, &st))
+	return NULL;
+
+    real_path = realpath(path, NULL);
+    if (!real_path)
+	return NULL;
+
+    /*
+     * Tricky business: we need the longest matching subpath
+     * which isn't a parent of the same subpath.
+     */
+    len = strlen(real_path);
+    matchlen = 0;
+    best = NULL;
+    for (m = head; m; m = m->next) {
+	if (m->hidden)
+	    continue;		/* Hidden underneath another mount */
+
+	if (m->pathlen > len)
+	    continue;		/* Cannot possibly match */
+
+	if (m->pathlen < matchlen)
+	    continue;		/* No point in testing this one */
+
+	if (st.st_dev == m->dev &&
+	    !memcmp(m->path, real_path, m->pathlen) &&
+	    (real_path[m->pathlen] == '/' || real_path[m->pathlen] == '\0')) {
+	    matchlen = m->pathlen;
+	    best = m;
+	}
+    }
+
+    if (best && subpath) {
+	if (real_path[best->pathlen] == '\0')
+	    *subpath = strdup("/");
+	else
+	    *subpath = strdup(real_path + best->pathlen);
+    }
+
+    return best;
+}
+
+#ifdef TEST
+
+int main(int argc, char *argv[])
+{
+    int i;
+    const struct mountinfo *m;
+    char *subpath;
+
+    parse_mountinfo();
+
+    for (i = 1; i < argc; i++) {
+	m = find_mount(argv[i], &subpath);
+	if (!m) {
+	    printf("%s: %s\n", argv[i], strerror(errno));
+	    continue;
+	}
+
+	printf("%s -> %s @ %s(%u,%u):%s %s %s\n",
+	       argv[i], subpath, m->devpath, major(m->dev), minor(m->dev),
+	       m->root, m->fstype, m->mountopt);
+	printf("Usable device: %s\n", find_device(m->dev, m->devpath));
+	free(subpath);
+    }
+
+    return 0;
+}
+
+#endif
diff --git a/com32/menu/menu.c b/extlinux/mountinfo.h
similarity index 50%
copy from com32/menu/menu.c
copy to extlinux/mountinfo.h
index 8f7af4d..9cbcac1 100644
--- a/com32/menu/menu.c
+++ b/extlinux/mountinfo.h
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2012 Intel Corporation; All Rights Reserved
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -10,35 +10,26 @@
  *
  * ----------------------------------------------------------------------- */
 
-/*
- * menu.c
- *
- * Simple menu system which displays a list and allows the user to select
- * a command line and/or edit it.
- */
-
-#include <consoles.h>
-#include "menu.h"
+#ifndef SYSLINUX_MOUNTINFO_H
+#define SYSLINUX_MOUNTINFO_H
 
-int draw_background(const char *arg)
-{
-    /* Nothing to do... */
-    (void)arg;
-    return 0;
-}
+#include <sys/types.h>
 
-void set_resolution(int x, int y)
-{
-    (void)x;
-    (void)y;
-}
+struct mountinfo {
+    struct mountinfo *next;
+    struct mountinfo *parent;
+    const char *root;
+    const char *path;
+    const char *fstype;
+    const char *devpath;
+    const char *mountopt;
+    int mountid;
+    int parentid;
+    int pathlen;
+    int hidden;
+    dev_t dev;
+};
 
-void local_cursor_enable(bool enabled)
-{
-    (void)enabled;
-}
+const struct mountinfo *find_mount(const char *path, char **subpath);
 
-void start_console(void)
-{
-    console_ansi_raw();
-}
+#endif /* SYSLINUX_MOUNTINFO_H */


More information about the Syslinux-commits mailing list