The output of btrfs subvolume list has changed in F18 to include generation,
which breaks the parsing in btrfs_subvolume_list. This change replaces sscanf
with a more robust regular expression. The new regular expression should also
handle the addition of future unexpected columns.
Fixes RHBZ#903620
---
daemon/Makefile.am | 6 +++--
daemon/btrfs.c | 67 ++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 56 insertions(+), 17 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a05771e..1fe8e12 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -197,14 +197,16 @@ guestfsd_LDADD = \
$(LIBSOCKET) \
$(LIB_CLOCK_GETTIME) \
$(LIBINTL) \
- $(SERVENT_LIB)
+ $(SERVENT_LIB) \
+ $(PCRE_LIBS)
guestfsd_CPPFLAGS = -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib
guestfsd_CFLAGS = \
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
$(AUGEAS_CFLAGS) \
$(HIVEX_CFLAGS) \
- $(YAJL_CFLAGS)
+ $(YAJL_CFLAGS) \
+ $(PCRE_CFLAGS)
# Manual pages and HTML files for the website.
man_MANS = guestfsd.8
diff --git a/daemon/btrfs.c b/daemon/btrfs.c
index 8ecde01..a940f0c 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <pcre.h>
#include <string.h>
#include <unistd.h>
@@ -326,7 +327,7 @@ do_btrfs_subvolume_list (const char *fs)
char *fs_buf;
const char *argv[MAX_ARGS];
size_t i = 0;
- char *out, *err, **lines, *pos;
+ char *out, *err, **lines;
size_t nr_subvolumes;
int r;
@@ -358,13 +359,18 @@ do_btrfs_subvolume_list (const char *fs)
/* Output is:
*
- * ID 256 top level 5 path test1
- * ID 257 top level 5 path dir/test2
- * ID 258 top level 5 path test3
+ * ID 256 gen 30 top level 5 path test1
+ * ID 257 gen 30 top level 5 path dir/test2
+ * ID 258 gen 30 top level 5 path test3
*
- * "ID <n>" is the subvolume ID. "top level <n>" is
the top level
- * subvolume ID. "path <str>" is the subvolume path, relative to
- * the top of the filesystem.
+ * "ID <n>" is the subvolume ID.
+ * "gen <n>" is the generation when the root was created or last
updated.
+ * "top level <n>" is the top level subvolume ID.
+ * "path <str>" is the subvolume path, relative to the top of the
filesystem.
+ *
+ * Note that the order that each of the above is fixed, but different versions
+ * of btrfs may display different sets of data. Specifically, older versions
+ * of btrfs do not display gen.
*/
nr_subvolumes = count_strings (lines);
@@ -384,33 +390,64 @@ do_btrfs_subvolume_list (const char *fs)
return NULL;
}
+ const char *errptr;
+ int erroffset;
+ pcre *re = pcre_compile ("ID\\s+(\\d+).*\\s"
+ "top level\\s+(\\d+).*\\s"
+ "path\\s(.*)",
+ 0, &errptr, &erroffset, NULL);
+ if (re == NULL) {
+ reply_with_error ("pcre_compile (%i): %s", erroffset, errptr);
+ free (ret->guestfs_int_btrfssubvolume_list_val);
+ free (ret);
+ free_stringslen (lines, nr_subvolumes);
+ return NULL;
+ }
+
for (i = 0; i < nr_subvolumes; ++i) {
/* To avoid allocations, reuse the 'line' buffer to store the
* path. Thus we don't need to free 'line', since it will be
* freed by the calling (XDR) code.
*/
char *line = lines[i];
+ #define N_MATCHES 3
+ int ovector[N_MATCHES * 3];
- if (sscanf (line, "ID %" SCNu64 " top level %" SCNu64 " path
",
- &ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_id,
-
&ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_top_level_id) != 2) {
+ if (pcre_exec (re, NULL, line, strlen(line), 0, 0,
+ ovector, N_MATCHES * 3) < 0)
+ #undef N_MATCHES
+ {
unexpected_output:
reply_with_error ("unexpected output from 'btrfs subvolume list'
command: %s", line);
free_stringslen (lines, nr_subvolumes);
free (ret->guestfs_int_btrfssubvolume_list_val);
free (ret);
+ pcre_free(re);
return NULL;
}
- pos = strstr (line, " path ");
- if (pos == NULL)
- goto unexpected_output;
- pos += 6;
+ #if __WORDSIZE == 64
+ #define STRTOU64 strtoul
+ #else
+ #define STRTOU64 strtoull
+ #endif
+
+ errno = 0;
+ ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_id =
+ STRTOU64(line + ovector[0], NULL, 10);
+ if (errno == ERANGE) goto unexpected_output;
+ ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_top_level_id =
+ STRTOU64(line + ovector[2], NULL, 10);
+ if (errno == ERANGE) goto unexpected_output;
- memmove (line, pos, strlen (pos) + 1);
+ #undef STRTOU64
+
+ memmove (line, line + ovector[4], ovector[5] + 1);
ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_path = line;
}
+ pcre_free(re);
+
return ret;
}
--
1.8.1