summaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-06-13 17:27:56 +0100
committerRichard W.M. Jones <rjones@redhat.com>2012-06-13 22:10:36 +0100
commitc0a087b8236755e95371d5c352c9d29a3ca992c0 (patch)
treed85c99f1f43fbd29c257ea57037509f742579ef4 /daemon
parent4165e28b53d7024c38a171586acfb5c5b2c858b7 (diff)
downloadlibguestfs-c0a087b8236755e95371d5c352c9d29a3ca992c0.tar.gz
libguestfs-c0a087b8236755e95371d5c352c9d29a3ca992c0.tar.xz
libguestfs-c0a087b8236755e95371d5c352c9d29a3ca992c0.zip
daemon: Fix order of devices in guestfs_list_devices when > 26 disks.
Sort the device names correctly, not just treating them as strings. As a result, /dev/sdz < /dev/sdaa.
Diffstat (limited to 'daemon')
-rw-r--r--daemon/daemon.h3
-rw-r--r--daemon/devsparts.c4
-rw-r--r--daemon/guestfsd.c64
3 files changed, 69 insertions, 2 deletions
diff --git a/daemon/daemon.h b/daemon/daemon.h
index af81a9da..b4790788 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -79,6 +79,9 @@ extern void sort_strings (char **argv, size_t len);
extern void free_strings (char **argv);
extern void free_stringslen (char **argv, size_t len);
+extern void sort_device_names (char **argv, size_t len);
+extern int compare_device_names (const char *a, const char *b);
+
extern char **split_lines (char *str);
#define command(out,err,name,...) commandf((out),(err),0,(name),__VA_ARGS__)
diff --git a/daemon/devsparts.c b/daemon/devsparts.c
index 1848c4f9..b6f755be 100644
--- a/daemon/devsparts.c
+++ b/daemon/devsparts.c
@@ -1,5 +1,5 @@
/* libguestfs - the guestfsd daemon
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2012 Red Hat Inc.
*
* 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
@@ -106,7 +106,7 @@ foreach_block_device (block_dev_func_t func)
/* Sort the devices. */
if (r.size > 0)
- sort_strings (r.argv, r.size);
+ sort_device_names (r.argv, r.size);
/* NULL terminate the list */
if (end_stringsbuf (&r) == -1) {
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index a7c111b0..b09f74e1 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -40,6 +40,7 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
+#include <assert.h>
#ifdef HAVE_PRINTF_H
# include <printf.h>
@@ -514,6 +515,69 @@ free_stringslen (char **argv, size_t len)
free (argv);
}
+/* Compare device names (including partition numbers if present).
+ * https://rwmj.wordpress.com/2011/01/09/how-are-linux-drives-named-beyond-drive-26-devsdz/
+ */
+int
+compare_device_names (const char *a, const char *b)
+{
+ size_t a_devlen, b_devlen;
+ int r;
+ int a_partnum, b_partnum;
+
+ /* Skip /dev/ prefix if present. */
+ if (STRPREFIX (a, "/dev/"))
+ a += 5;
+ if (STRPREFIX (b, "/dev/"))
+ b += 5;
+
+ /* Skip sd/hd/vd. */
+ assert (a[1] == 'd');
+ a += 2;
+ assert (b[1] == 'd');
+ b += 2;
+
+ /* Get device name part, that is, just 'a', 'ab' etc. */
+ a_devlen = strcspn (a, "0123456789");
+ b_devlen = strcspn (b, "0123456789");
+
+ /* If device name part is longer, it is always greater, eg.
+ * "/dev/sdz" < "/dev/sdaa".
+ */
+ if (a_devlen != b_devlen)
+ return a_devlen - b_devlen;
+
+ /* Device name parts are the same length, so do a regular compare. */
+ r = strncmp (a, b, a_devlen);
+ if (r != 0)
+ return r;
+
+ /* Compare partitions numbers. */
+ a += a_devlen;
+ b += a_devlen;
+
+ r = sscanf (a, "%d", &a_partnum);
+ assert (r == 1);
+ r = sscanf (b, "%d", &b_partnum);
+ assert (r == 1);
+
+ return a_partnum - b_partnum;
+}
+
+static int
+compare_device_names_vp (const void *vp1, const void *vp2)
+{
+ char * const *p1 = (char * const *) vp1;
+ char * const *p2 = (char * const *) vp2;
+ return compare_device_names (*p1, *p2);
+}
+
+void
+sort_device_names (char **argv, size_t len)
+{
+ qsort (argv, len, sizeof (char *), compare_device_names_vp);
+}
+
/* Easy ways to run external commands. For full documentation, see
* 'commandrvf' below.
*/