[PATCH] btrfs: Fix btrfs_subvolume_list on F18
by Matthew Booth
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
---
appliance/packagelist.in | 3 +
daemon/Makefile.am | 6 +-
daemon/btrfs.c | 156 ++++++++++++++++++++++++++++++-----------------
3 files changed, 108 insertions(+), 57 deletions(-)
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index ead6d41..a43039c 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -42,6 +42,7 @@
ntfsprogs
ntfs-3g
openssh-clients
+ pcre
reiserfs-utils
libselinux
systemd /* for /sbin/reboot and udevd */
@@ -65,6 +66,7 @@
libaugeas0
libcap2
libhivex0
+ libpcre3
libyajl2
linux-image
nilfs-tools
@@ -95,6 +97,7 @@
nilfs-utils
ntfsprogs
ntfs-3g
+ pcre
reiserfsprogs
systemd
vim
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 1a1626f..b7731ac 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -198,14 +198,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..f13d025 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -21,12 +21,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <pcre.h>
#include <string.h>
#include <unistd.h>
#include "daemon.h"
#include "actions.h"
#include "optgroups.h"
+#include "xstrtol.h"
GUESTFSD_EXT_CMD(str_btrfs, btrfs);
GUESTFSD_EXT_CMD(str_btrfstune, btrfstune);
@@ -321,97 +323,141 @@ do_btrfs_subvolume_create (const char *dest)
guestfs_int_btrfssubvolume_list *
do_btrfs_subvolume_list (const char *fs)
{
- const size_t MAX_ARGS = 64;
- guestfs_int_btrfssubvolume_list *ret;
- char *fs_buf;
- const char *argv[MAX_ARGS];
- size_t i = 0;
- char *out, *err, **lines, *pos;
- size_t nr_subvolumes;
- int r;
-
- fs_buf = sysroot_path (fs);
- if (fs_buf == NULL) {
- reply_with_perror ("malloc");
- return NULL;
- }
+ char **lines;
- ADD_ARG (argv, i, str_btrfs);
- ADD_ARG (argv, i, "subvolume");
- ADD_ARG (argv, i, "list");
- ADD_ARG (argv, i, fs_buf);
- ADD_ARG (argv, i, NULL);
+ /* Execute 'btrfs subvolume list <fs>', and split the output into lines */
+ {
+ char *fs_buf = sysroot_path (fs);
+ if (fs_buf == NULL) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
- r = commandv (&out, &err, argv);
- free (fs_buf);
- if (r == -1) {
- reply_with_error ("%s: %s", fs, err);
+ size_t i = 0;
+ const size_t MAX_ARGS = 64;
+ const char *argv[MAX_ARGS];
+ ADD_ARG (argv, i, str_btrfs);
+ ADD_ARG (argv, i, "subvolume");
+ ADD_ARG (argv, i, "list");
+ ADD_ARG (argv, i, fs_buf);
+ ADD_ARG (argv, i, NULL);
+
+ char *out, *err;
+ int r = commandv (&out, &err, argv);
+ free (fs_buf);
+ if (r == -1) {
+ reply_with_error ("%s: %s", fs, err);
+ free (err);
+ return NULL;
+ }
free (err);
- return NULL;
- }
- free (err);
- lines = split_lines (out);
- free (out);
- if (!lines)
- return NULL;
+ lines = split_lines (out);
+ free (out); out = NULL;
+ if (!lines)
+ return NULL;
+ }
/* 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);
+
+ guestfs_int_btrfssubvolume_list *ret = NULL;
+ pcre *re = NULL;
+
+ size_t nr_subvolumes = count_strings (lines);
ret = malloc (sizeof *ret);
if (!ret) {
reply_with_perror ("malloc");
- free_stringslen (lines, nr_subvolumes);
- return NULL;
+ goto error;
}
+
ret->guestfs_int_btrfssubvolume_list_len = nr_subvolumes;
ret->guestfs_int_btrfssubvolume_list_val =
calloc (nr_subvolumes, sizeof (struct guestfs_int_btrfssubvolume));
if (ret->guestfs_int_btrfssubvolume_list_val == NULL) {
reply_with_perror ("malloc");
- free (ret);
- free_stringslen (lines, nr_subvolumes);
- return NULL;
+ goto error;
}
- for (i = 0; i < nr_subvolumes; ++i) {
+ const char *errptr;
+ int erroffset;
+ 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);
+ goto error;
+ }
+
+ for (size_t 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 4
+ 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);
- return NULL;
+ goto error;
}
- pos = strstr (line, " path ");
- if (pos == NULL)
+ struct guestfs_int_btrfssubvolume *this =
+ &ret->guestfs_int_btrfssubvolume_list_val[i];
+
+ #if __WORDSIZE == 64
+ #define XSTRTOU64 xstrtoul
+ #else
+ #define XSTRTOU64 xstrtoull
+ #endif
+
+ if (XSTRTOU64 (line + ovector[2], NULL, 10,
+ &this->btrfssubvolume_id, NULL) != LONGINT_OK)
+ goto unexpected_output;
+ if (XSTRTOU64 (line + ovector[4], NULL, 10,
+ &this->btrfssubvolume_top_level_id, NULL) != LONGINT_OK)
goto unexpected_output;
- pos += 6;
- memmove (line, pos, strlen (pos) + 1);
- ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_path = line;
+ #undef XSTRTOU64
+
+ memmove (line, line + ovector[6], ovector[7] + 1);
+ this->btrfssubvolume_path = line;
}
+ free (lines);
+ pcre_free (re);
+
return ret;
+
+error:
+ free_stringslen (lines, nr_subvolumes);
+ if (ret) free (ret->guestfs_int_btrfssubvolume_list_val);
+ free (ret);
+ if (re) pcre_free (re);
+
+ return NULL;
}
int
--
1.8.1
11 years, 11 months
[PATCH 1/2] lib: Add CLEANUP_FREE macro which automatically calls 'free' when leaving scope.
by Richard W.M. Jones
From: "Richard W.M. Jones" <rjones(a)redhat.com>
Use the macro like this to create temporary variables which are
automatically cleaned up when the scope is exited:
{
CLEANUP_FREE (char *, foo, strdup (bar)); /* char *foo = strdup (bar) */
...
// no need to call free (foo)!
}
On GCC and LLVM, this is implemented using __attribute__((cleanup(...))).
On other compilers, we fall back to a less efficient implementation
which saves up the memory allocations and frees them all when the
handle is closed.
---
configure.ac | 35 +++++++++++++++++++++++++++++++++++
src/alloc.c | 19 +++++++++++++++++++
src/guestfs-internal.h | 33 +++++++++++++++++++++++++++++++++
src/handle.c | 15 +++++++++++++++
4 files changed, 102 insertions(+)
diff --git a/configure.ac b/configure.ac
index 39c79cf..7745ab5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -191,6 +191,41 @@ AC_SYS_LARGEFILE
dnl Check sizeof long.
AC_CHECK_SIZEOF([long])
+dnl Check if __attribute__((cleanup(...))) works.
+dnl XXX It would be nice to use AC_COMPILE_IFELSE here, but gcc just
+dnl emits a warning for attributes that it doesn't understand.
+AC_MSG_CHECKING([if __attribute__((cleanup(...))) works with this compiler])
+AC_RUN_IFELSE([
+AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+freep (void *ptr)
+{
+ exit (0);
+}
+
+void
+test (void)
+{
+ __attribute__((cleanup(freep))) char *ptr = malloc (100);
+}
+
+int
+main (int argc, char *argv[])
+{
+ test ();
+ exit (1);
+}
+]])
+ ],[
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_ATTRIBUTE_CLEANUP],[1],[Define to 1 if '__attribute__((cleanup(...)))' works with this compiler.])
+ ],[
+ AC_MSG_RESULT([no])
+ ])
+
dnl Check if dirent (readdir) supports d_type member.
AC_STRUCT_DIRENT_D_TYPE
diff --git a/src/alloc.c b/src/alloc.c
index 25d7f42..cf3741a 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -122,3 +122,22 @@ guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...)
return msg;
}
+
+void
+guestfs___cleanup_free (void *ptr)
+{
+ free (* (void **) ptr);
+}
+
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+void
+guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data)
+{
+ struct deferred_free *p = safe_malloc (g, sizeof *p);
+
+ p->freefn = freefn;
+ p->data = data;
+ p->next = g->deferred_frees;
+ g->deferred_frees = p;
+}
+#endif
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 870207b..d27a3c2 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -72,6 +72,19 @@
#define TRACE4(name, arg1, arg2, arg3, arg4)
#endif
+#ifdef HAVE_ATTRIBUTE_CLEANUP
+#define CLEANUP(type, var, init, freefn) \
+ __attribute__((cleanup(freefn))) type var = init
+#else
+#define CLEANUP(type, var, init, freefn) \
+ type var = init; \
+ guestfs___defer_free ((g), (freefn), (var))
+#endif
+
+#define CLEANUP_FREE(type, var, init) \
+ CLEANUP(type, var, (init), guestfs___cleanup_free)
+/* other CLEANUP_* macros to follow */
+
#define TMP_TEMPLATE_ON_STACK(g,var) \
char *ttos_tmpdir = guestfs_get_tmpdir (g); \
char var[strlen (ttos_tmpdir) + 32]; \
@@ -324,6 +337,10 @@ struct guestfs_h
virConnectCredentialPtr requested_credentials;
#endif
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+ struct deferred_free *deferred_frees;
+#endif
+
/**** Private data for attach-methods. ****/
/* NB: This cannot be a union because of a pathological case where
* the user changes attach-method while reusing the handle to launch
@@ -356,6 +373,14 @@ struct guestfs_h
#endif
};
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+struct deferred_free {
+ struct deferred_free *next;
+ void (*freefn) (void *);
+ void *data;
+};
+#endif
+
/* Per-filesystem data stored for inspect_os. */
enum inspect_os_format {
OS_FORMAT_UNKNOWN = 0,
@@ -474,6 +499,14 @@ extern char *guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...)
#define safe_memdup guestfs___safe_memdup
#define safe_asprintf guestfs___safe_asprintf
+/* These functions are used internally by the CLEANUP_* macros.
+ * Don't call them directly.
+ */
+extern void guestfs___cleanup_free (void *ptr);
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+extern void guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data);
+#endif
+
/* errors.c */
extern void guestfs___init_error_handler (guestfs_h *g);
diff --git a/src/handle.c b/src/handle.c
index 39e30c7..8cf7a40 100644
--- a/src/handle.c
+++ b/src/handle.c
@@ -253,6 +253,9 @@ void
guestfs_close (guestfs_h *g)
{
struct qemu_param *qp, *qp_next;
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+ struct deferred_free *dfp, *dfp_next;
+#endif
guestfs_h **gg;
if (g->state == NO_HANDLE) {
@@ -324,6 +327,18 @@ guestfs_close (guestfs_h *g)
while (g->error_cb_stack)
guestfs_pop_error_handler (g);
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+ /* For compilers that don't support __attribute__((cleanup(...))),
+ * free any temporary data that we allocated in CLEANUP_* macros
+ * here.
+ */
+ for (dfp = g->deferred_frees; dfp; dfp = dfp_next) {
+ dfp->freefn (&dfp->data);
+ dfp_next = dfp->next;
+ free (dfp);
+ }
+#endif
+
if (g->pda)
hash_free (g->pda);
free (g->tmpdir);
--
1.8.1
11 years, 11 months
[PATCH] btrfs: Fix btrfs_subvolume_list on F18
by Matthew Booth
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 | 156 ++++++++++++++++++++++++++++++++++-------------------
2 files changed, 105 insertions(+), 57 deletions(-)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a05771e..d70dbd7 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..f13d025 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -21,12 +21,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
+#include <pcre.h>
#include <string.h>
#include <unistd.h>
#include "daemon.h"
#include "actions.h"
#include "optgroups.h"
+#include "xstrtol.h"
GUESTFSD_EXT_CMD(str_btrfs, btrfs);
GUESTFSD_EXT_CMD(str_btrfstune, btrfstune);
@@ -321,97 +323,141 @@ do_btrfs_subvolume_create (const char *dest)
guestfs_int_btrfssubvolume_list *
do_btrfs_subvolume_list (const char *fs)
{
- const size_t MAX_ARGS = 64;
- guestfs_int_btrfssubvolume_list *ret;
- char *fs_buf;
- const char *argv[MAX_ARGS];
- size_t i = 0;
- char *out, *err, **lines, *pos;
- size_t nr_subvolumes;
- int r;
-
- fs_buf = sysroot_path (fs);
- if (fs_buf == NULL) {
- reply_with_perror ("malloc");
- return NULL;
- }
+ char **lines;
- ADD_ARG (argv, i, str_btrfs);
- ADD_ARG (argv, i, "subvolume");
- ADD_ARG (argv, i, "list");
- ADD_ARG (argv, i, fs_buf);
- ADD_ARG (argv, i, NULL);
+ /* Execute 'btrfs subvolume list <fs>', and split the output into lines */
+ {
+ char *fs_buf = sysroot_path (fs);
+ if (fs_buf == NULL) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
- r = commandv (&out, &err, argv);
- free (fs_buf);
- if (r == -1) {
- reply_with_error ("%s: %s", fs, err);
+ size_t i = 0;
+ const size_t MAX_ARGS = 64;
+ const char *argv[MAX_ARGS];
+ ADD_ARG (argv, i, str_btrfs);
+ ADD_ARG (argv, i, "subvolume");
+ ADD_ARG (argv, i, "list");
+ ADD_ARG (argv, i, fs_buf);
+ ADD_ARG (argv, i, NULL);
+
+ char *out, *err;
+ int r = commandv (&out, &err, argv);
+ free (fs_buf);
+ if (r == -1) {
+ reply_with_error ("%s: %s", fs, err);
+ free (err);
+ return NULL;
+ }
free (err);
- return NULL;
- }
- free (err);
- lines = split_lines (out);
- free (out);
- if (!lines)
- return NULL;
+ lines = split_lines (out);
+ free (out); out = NULL;
+ if (!lines)
+ return NULL;
+ }
/* 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);
+
+ guestfs_int_btrfssubvolume_list *ret = NULL;
+ pcre *re = NULL;
+
+ size_t nr_subvolumes = count_strings (lines);
ret = malloc (sizeof *ret);
if (!ret) {
reply_with_perror ("malloc");
- free_stringslen (lines, nr_subvolumes);
- return NULL;
+ goto error;
}
+
ret->guestfs_int_btrfssubvolume_list_len = nr_subvolumes;
ret->guestfs_int_btrfssubvolume_list_val =
calloc (nr_subvolumes, sizeof (struct guestfs_int_btrfssubvolume));
if (ret->guestfs_int_btrfssubvolume_list_val == NULL) {
reply_with_perror ("malloc");
- free (ret);
- free_stringslen (lines, nr_subvolumes);
- return NULL;
+ goto error;
}
- for (i = 0; i < nr_subvolumes; ++i) {
+ const char *errptr;
+ int erroffset;
+ 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);
+ goto error;
+ }
+
+ for (size_t 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 4
+ 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);
- return NULL;
+ goto error;
}
- pos = strstr (line, " path ");
- if (pos == NULL)
+ struct guestfs_int_btrfssubvolume *this =
+ &ret->guestfs_int_btrfssubvolume_list_val[i];
+
+ #if __WORDSIZE == 64
+ #define XSTRTOU64 xstrtoul
+ #else
+ #define XSTRTOU64 xstrtoull
+ #endif
+
+ if (XSTRTOU64 (line + ovector[2], NULL, 10,
+ &this->btrfssubvolume_id, NULL) != LONGINT_OK)
+ goto unexpected_output;
+ if (XSTRTOU64 (line + ovector[4], NULL, 10,
+ &this->btrfssubvolume_top_level_id, NULL) != LONGINT_OK)
goto unexpected_output;
- pos += 6;
- memmove (line, pos, strlen (pos) + 1);
- ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_path = line;
+ #undef XSTRTOU64
+
+ memmove (line, line + ovector[6], ovector[7] + 1);
+ this->btrfssubvolume_path = line;
}
+ free (lines);
+ pcre_free (re);
+
return ret;
+
+error:
+ free_stringslen (lines, nr_subvolumes);
+ if (ret) free (ret->guestfs_int_btrfssubvolume_list_val);
+ free (ret);
+ if (re) pcre_free (re);
+
+ return NULL;
}
int
--
1.8.1
11 years, 11 months
Libguestfs with Perl 6 :-)
by Daniel P. Berrange
I saw the latest parrot release notice on LWN this week and randomly
wondered the Perl6 native call interface was like compared to the
horrific mess that is Perl5 XS. The answer is that it is a thing of
beauty !
Here is the libguestfs hello world example in Perl6
$ dd if=/dev/zero of=guest.img bs=1M count=100
$ mke2fs guest.img
$ cat guestfs.p6
use NativeCall;
say "Defining stubs";
sub guestfs_create() returns OpaquePointer is native('libguestfs') {}
sub guestfs_add_drive(OpaquePointer $handle, Str $path) returns Int is native('libguestfs') {}
sub guestfs_launch(OpaquePointer $handle) returns Int is native('libguestfs') {}
sub guestfs_mount(OpaquePointer $handle, Str $path, Str $target) returns Int is native('libguestfs') {}
sub guestfs_touch(OpaquePointer $handle, Str $path) returns Int is native('libguestfs') {}
sub guestfs_umount(OpaquePointer $handle, Str $path) returns Int is native('libguestfs') {}
sub guestfs_shutdown(OpaquePointer $handle) returns Int is native('libguestfs') {}
sub guestfs_close(OpaquePointer $handle) returns Int is native('libguestfs') {}
say "Creating handle";
my $g = guestfs_create();
say "Adding drive";
guestfs_add_drive($g, "guest.img");
say "Launching appliance";
guestfs_launch($g);
say "Mounting root";
guestfs_mount($g, "/dev/sda", "/");
say "Touching hello";
guestfs_touch($g, "/hello");
say "Unmounting root";
guestfs_umount($g, "/");
say "Shutting down appliance";
guestfs_shutdown($g);
say "Closing handle";
guestfs_close($g);
$ perl6 guestfs.p6
Defining stubs
Creating handle
Adding drive
Launching appliance
Mounting root
Touching hello
Unmounting root
Shutting down appliance
Closing handle
$ su -
# mount -o loop /home/berrange/guest.img /mnt/
# ls /mnt/
hello lost+found
Obviously for real world usage, we'd want to have the generator create
a module containing all the stub definitions. I've at least answered
my curiosity now :-)
If only someone would port the Perl6 'NativeCall' module to Perl5, we
could kill of the XS horrificness there too
Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
11 years, 11 months
[PATCH] python: Inherit from 'object' base class.
by Richard W.M. Jones
From: "Richard W.M. Jones" <rjones(a)redhat.com>
This fixes the following Debian bug:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=698771
For an overview, see this question and the links from there:
http://stackoverflow.com/questions/4015417/python-class-inherits-object
---
README | 2 +-
generator/python.ml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README b/README
index 28db6c5..0b207a3 100644
--- a/README
+++ b/README
@@ -137,7 +137,7 @@ To build language bindings:
- Perl if you want to build the perl bindings (optional)
-- Python if you want to build the python bindings (optional)
+- Python >= 2.2 if you want to build the python bindings (optional)
- Ruby, rake if you want to build the ruby bindings (optional)
diff --git a/generator/python.ml b/generator/python.ml
index 8752fd3..0b0a79c 100644
--- a/generator/python.ml
+++ b/generator/python.ml
@@ -662,7 +662,7 @@ import libguestfsmod
class ClosedHandle(ValueError):
pass
-class GuestFS:
+class GuestFS(object):
\"\"\"Instances of this class are libguestfs API handles.\"\"\"
def __init__ (self, environment=True, close_on_exit=True):
--
1.8.1
11 years, 11 months
[REVIEW ONLY] Mountable patches
by Matthew Booth
These 3 patches implement support for APIs which must accept a mountable, but
don't update apis which must return mountables.
Matt
11 years, 11 months
[PATCH] btrfs: Fix btrfs_subvolume_list on F18
by Matthew Booth
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
11 years, 11 months
Inspect running disk image
by NoxDaFox
Greetings,
I'd like to monitor the FS activities (read/write) at runtime on
several virtual disk images running on Qemu KVM.
The aim is to periodically inspect these images to identify possible
Windows registry modification, file creation and so on..
What should be the optimal procedure? Shall I launch a new handler
each time? It is a quite expensive procedure and it takes a lot of
time on loaded systems.
Would be enough to mount/unmount the disk at each read?
noxdafox
11 years, 11 months
APIs affected by btrfs subvolumes
by Matthew Booth
We have a problem with btrfs subvolumes, as they can hold a filesystem
which can be mounted, but aren't describable using only the name of a
block device. Specifically they need at least a block device and a
subvolume name.
There are several APIs which operate on filesystems which currently
describe it using only a block device. I've listed them all below.
In all cases, these APIs need to use a more fully specified 'mountable'
object. There are currently 2 cases we need to support: bare device, and
device + subvolume. As this is a significant API change, I believe we
should also allow the mountable description to be safely extended
without breaking the api again.
I propose a string which contains a descriptor followed by data custom
to the descriptor. For the 2 existing cases this would be:
device:/dev/sda3
subvol:/dev/sda1,root
Any other solution must convey the same information. Could we achieve
this with a union, for e.g.? How would this work with bindings?
I would deprecate all of the apis below and replace them with
alternative versions with a _ext suffix. The replacement apis would
accept and return the enhanced descriptor.
Inspection APIS:
All take a root filesystem as an argument, which may not be a block
device.
inspect_get_arch
inspect_get_distro
inspect_get_drive_mappings
inspect_get_filesystems
inspect_get_format
inspect_get_hostname
inspect_get_icon
inspect_get_major_version
inspect_get_minor_version
inspect_get_mountpoints
inspect_get_package_format
inspect_get_package_management
inspect_get_product_name
inspect_get_product_variant
inspect_get_type
inspect_get_windows_current_control_set
inspect_get_windows_systemroot
inspect_is_live
inspect_is_multipart
inspect_is_netinst
inspect_list_applications
inspect_list_applications2
The following return filesystem descriptors.
inspect_get_filesystems
inspect_get_roots
inspect_os
Mount APIS:
All currently take a block device, but need to accept any filesystem
descriptor.
mount
mount_options
mount_ro
mount_vfs
Label APIs:
All these currently take a block device, but are arguably applicable to
btrfs subvolumes. A subvolume doesn't have a label itself, but its root
filesystem does. A btrfs subvolume can still be mounted by label, as
long as the subvolume is also given.
set_label: note that this would also affect other subvolumes
vfs_label
vfs_type
vfs_uuid
Device name mangling:
These apis are used to return sanitised device names. I'm not 100%
convinced we need to modify them. However, we may want additional apis
which handle filesystem descriptors.
canonical_device_name
part_to_dev
Misc:
zerofree: only actually supports ext2 and ext3, but the operation makes
sense for any filesystem. We should update the api and consider the
implementation later.
findfs_label
findfs_uuid: These return a device which contains the btrfs filesystem.
This device will be mountable, even if the filesystem spans many
devices. Not 100% convinced about this one, although it feels deficient.
11 years, 11 months
[PATCH] docs: Fix cut/paste error in pread_device
by Matthew Booth
---
generator/actions.ml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/generator/actions.ml b/generator/actions.ml
index 9e8f017..d75de8d 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -8240,7 +8240,7 @@ See also C<guestfs_pwrite>." };
];
shortdesc = "read part of a device";
longdesc = "\
-This command lets you read part of a file. It reads C<count>
+This command lets you read part of a block device. It reads C<count>
bytes of C<device>, starting at C<offset>.
This may read fewer bytes than requested. For further details
--
1.8.1
11 years, 11 months