diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2012-06-13 17:27:56 +0100 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2012-06-13 22:10:36 +0100 |
commit | c0a087b8236755e95371d5c352c9d29a3ca992c0 (patch) | |
tree | d85c99f1f43fbd29c257ea57037509f742579ef4 | |
parent | 4165e28b53d7024c38a171586acfb5c5b2c858b7 (diff) | |
download | libguestfs-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.
-rw-r--r-- | daemon/daemon.h | 3 | ||||
-rw-r--r-- | daemon/devsparts.c | 4 | ||||
-rw-r--r-- | daemon/guestfsd.c | 64 |
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. */ |