[Pkg-libvirt-commits] [libguestfs] 144/233: New API: disk-create for creating blank disks.

Hilko Bengen bengen at moszumanska.debian.org
Wed Feb 19 21:11:50 UTC 2014


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to branch experimental
in repository libguestfs.

commit 588af1953e5f7ab74009b9175cc5d3efb8bb651a
Author: Richard W.M. Jones <rjones at redhat.com>
Date:   Tue Jan 28 13:09:06 2014 +0000

    New API: disk-create for creating blank disks.
    
    This is a wrapper around either 'qemu-img create' or calls to open,
    truncate and posix_fallocate which litter and complicate existing
    code.
---
 Makefile.am                      |   1 +
 configure.ac                     |   1 +
 generator/actions.ml             |  52 ++++++-
 gobject/Makefile.inc             |   2 +
 po/POTFILES                      |   2 +
 src/Makefile.am                  |   1 +
 src/create.c                     | 319 +++++++++++++++++++++++++++++++++++++++
 tests/create/Makefile.am         |  27 ++++
 tests/create/test-disk-create.sh | 149 ++++++++++++++++++
 9 files changed, 552 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index e39d11f..5b8f109 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,7 @@ SUBDIRS += tests/tmpdirs
 SUBDIRS += tests/protocol
 SUBDIRS += tests/events
 SUBDIRS += tests/parallel
+SUBDIRS += tests/create
 SUBDIRS += tests/disks
 SUBDIRS += tests/mountable
 SUBDIRS += tests/network
diff --git a/configure.ac b/configure.ac
index c07462e..df8e962 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1745,6 +1745,7 @@ AC_CONFIG_FILES([Makefile
                  tests/btrfs/Makefile
                  tests/c-api/Makefile
                  tests/charsets/Makefile
+                 tests/create/Makefile
                  tests/data/Makefile
                  tests/disks/Makefile
                  tests/disks/test-qemu-drive-libvirt.xml
diff --git a/generator/actions.ml b/generator/actions.ml
index fa1a2c5..176de98 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -3093,6 +3093,54 @@ Return the current backend settings.
 
 See L<guestfs(3)/BACKEND>, L<guestfs(3)/BACKEND SETTINGS>." };
 
+  { defaults with
+    name = "disk_create";
+    style = RErr, [String "filename"; String "format"; Int64 "size"], [OString "backingfile"; OString "backingformat"; OString "preallocation"; OString "compat"; OInt "clustersize"];
+    test_excuse = "tests in tests/create subdirectory";
+    shortdesc = "create a blank disk image";
+    longdesc = "\
+Create a blank disk image called C<filename> (a host file)
+with format C<format> (usually C<raw> or C<qcow2>).
+The size is C<size> bytes.
+
+If used with the optional C<backingfile> parameter, then a snapshot
+is created on top of the backing file.  In this case, C<size> must
+be passed as C<-1>.  The size of the snapshot is the same as the
+size of the backing file, which is discovered automatically.  You
+are encouraged to also pass C<backingformat> to describe the format
+of C<backingfile>.
+
+The other optional parameters are:
+
+=over 4
+
+=item C<preallocation>
+
+If format is C<raw>, then this can be either C<sparse> or C<full>
+to create a sparse or fully allocated file respectively.  The default
+is C<sparse>.
+
+If format is C<qcow2>, then this can be either C<off> or
+C<metadata>.  Preallocating metadata can be faster when doing lots
+of writes, but uses more space.  The default is C<off>.
+
+=item C<compat>
+
+C<qcow2> only:
+Pass the string C<1.1> to use the advanced qcow2 format supported
+by qemu E<ge> 1.1.
+
+=item C<clustersize>
+
+C<qcow2> only:
+Change the qcow2 cluster size.  The default is 65536 (bytes) and
+this setting may be any power of two between 512 and 2097152.
+
+=back
+
+Note that this call does not add the new disk to the handle.  You
+may need to call C<guestfs_add_drive_opts> separately." };
+
 ]
 
 (* daemon_functions are any functions which cause some action
@@ -11739,7 +11787,7 @@ let fish_commands = [
 This creates an empty (zeroed) file of the given size, and then adds
 so it can be further examined.
 
-For more advanced image creation, see L<qemu-img(1)> utility.
+For more advanced image creation, see L</disk-create>.
 
 Size can be specified using standard suffixes, eg. C<1M>.
 
@@ -11983,7 +12031,7 @@ not assigned to the file until they are needed.  Sparse disk files
 only use space when written to, but they are slower and there is a
 danger you could run out of real disk space during a write operation.
 
-For more advanced image creation, see L<qemu-img(1)> utility.
+For more advanced image creation, see L</disk-create>.
 
 Size can be specified using standard suffixes, eg. C<1M>.
 
diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc
index 7024754..7a8493a 100644
--- a/gobject/Makefile.inc
+++ b/gobject/Makefile.inc
@@ -52,6 +52,7 @@ guestfs_gobject_headers= \
   include/guestfs-gobject/optargs-mount_local.h \
   include/guestfs-gobject/optargs-umount_local.h \
   include/guestfs-gobject/optargs-add_drive_scratch.h \
+  include/guestfs-gobject/optargs-disk_create.h \
   include/guestfs-gobject/optargs-is_file.h \
   include/guestfs-gobject/optargs-is_dir.h \
   include/guestfs-gobject/optargs-umount.h \
@@ -127,6 +128,7 @@ guestfs_gobject_sources= \
   src/optargs-mount_local.c \
   src/optargs-umount_local.c \
   src/optargs-add_drive_scratch.c \
+  src/optargs-disk_create.c \
   src/optargs-is_file.c \
   src/optargs-is_dir.c \
   src/optargs-umount.c \
diff --git a/po/POTFILES b/po/POTFILES
index c6277e9..76d1b1d 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -170,6 +170,7 @@ gobject/src/optargs-copy_device_to_device.c
 gobject/src/optargs-copy_device_to_file.c
 gobject/src/optargs-copy_file_to_device.c
 gobject/src/optargs-copy_file_to_file.c
+gobject/src/optargs-disk_create.c
 gobject/src/optargs-e2fsck.c
 gobject/src/optargs-fstrim.c
 gobject/src/optargs-grep.c
@@ -267,6 +268,7 @@ src/canonical-name.c
 src/cleanup.c
 src/command.c
 src/conn-socket.c
+src/create.c
 src/dbdump.c
 src/drives.c
 src/errnostring-gperf.c
diff --git a/src/Makefile.am b/src/Makefile.am
index ba02061..4475281 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -90,6 +90,7 @@ libguestfs_la_SOURCES = \
 	canonical-name.c \
 	command.c \
 	conn-socket.c \
+	create.c \
 	dbdump.c \
 	drives.c \
 	errors.c \
diff --git a/src/create.c b/src/create.c
new file mode 100644
index 0000000..40a5cac
--- /dev/null
+++ b/src/create.c
@@ -0,0 +1,319 @@
+/* libguestfs
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+#include "guestfs_protocol.h"
+
+static int disk_create_raw (guestfs_h *g, const char *filename, int64_t size, const struct guestfs_disk_create_argv *optargs);
+static int disk_create_qcow2 (guestfs_h *g, const char *filename, int64_t size, const char *backingfile, const struct guestfs_disk_create_argv *optargs);
+static char *qemu_escape_param (guestfs_h *g, const char *param);
+
+int
+guestfs__disk_create (guestfs_h *g, const char *filename,
+                      const char *format, int64_t size,
+                      const struct guestfs_disk_create_argv *optargs)
+{
+  const char *backingfile;
+
+  backingfile = optargs->bitmask & GUESTFS_DISK_CREATE_BACKINGFILE_BITMASK ?
+    optargs->backingfile : NULL;
+
+  /* Ensure size is valid. */
+  if (backingfile) {
+    if (size != -1) {
+      error (g, _("if using a backing file, size must be passed as -1"));
+      return -1;
+    }
+  } else {
+    /* XXX Actually size == 0 could be valid, although not useful and
+     * it causes qemu to break.
+     */
+    if (size <= 0) {
+      error (g, _("invalid size: %" PRIi64), size);
+      return -1;
+    }
+  }
+
+  /* Now the format-specific code. */
+  if (STREQ (format, "raw")) {
+    if (backingfile) {
+      error (g, _("backingfile cannot be used for raw format disks"));
+      return -1;
+    }
+    if (disk_create_raw (g, filename, size, optargs) == -1)
+      return -1;
+  }
+  else if (STREQ (format, "qcow2")) {
+    if (disk_create_qcow2 (g, filename, size, backingfile, optargs) == -1)
+      return -1;
+  }
+  else {
+    /* Be conservative about what formats we support, since we don't
+     * want to make unlimited promises through the API.  We can always
+     * add more later.
+     */
+    error (g, _("unsupported format '%s'"), format);
+    return -1;
+  }
+
+  return 0;
+}
+
+static int
+disk_create_raw (guestfs_h *g, const char *filename, int64_t size,
+                 const struct guestfs_disk_create_argv *optargs)
+{
+  int allocated = 0;
+  int fd;
+  struct stat statbuf;
+
+  /* backingfile parameter not present checked above */
+
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_BACKINGFORMAT_BITMASK) {
+    error (g, _("backingformat parameter cannot be used with raw format"));
+    return -1;
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK) {
+    if (STREQ (optargs->preallocation, "sparse"))
+      allocated = 0;
+    else if (STREQ (optargs->preallocation, "full"))
+      allocated = 1;
+    else {
+      error (g, _("invalid value for preallocation parameter '%s'"),
+             optargs->preallocation);
+      return -1;
+    }
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_COMPAT_BITMASK) {
+    error (g, _("compat parameter cannot be used with raw format"));
+    return -1;
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_CLUSTERSIZE_BITMASK) {
+    error (g, _("clustersize parameter cannot be used with raw format"));
+    return -1;
+  }
+
+  /* This version refuses to overwrite block devices or char devices.
+   * XXX It would be possible to make it work with block devices.
+   */
+  if (stat (filename, &statbuf) == 0) {
+    if (S_ISBLK (statbuf.st_mode)) {
+      error (g, _("refusing to overwrite block device '%s'"), filename);
+      return -1;
+    }
+    if (S_ISCHR (statbuf.st_mode)) {
+      error (g, _("refusing to overwrite char device '%s'"), filename);
+      return -1;
+    }
+  }
+
+  fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC|O_CLOEXEC, 0666);
+  if (fd == -1) {
+    perrorf (g, _("cannot create raw file: %s"), filename);
+    return -1;
+  }
+
+  if (!allocated) {             /* Sparse file. */
+    if (ftruncate (fd, size) == -1) {
+      perrorf (g, _("%s: truncate"), filename);
+      close (fd);
+      unlink (filename);
+      return -1;
+    }
+  }
+  else {                        /* Allocated file. */
+#ifdef HAVE_POSIX_FALLOCATE
+    int err;
+
+    err = posix_fallocate (fd, 0, size);
+    if (err != 0) {
+      errno = err;
+      perrorf (g, _("%s: fallocate"), filename);
+      close (fd);
+      unlink (filename);
+      return -1;
+    }
+#else
+    /* Slow emulation of posix_fallocate on platforms which don't have it. */
+    char buffer[BUFSIZ];
+    size_t remaining = size;
+    size_t n;
+    ssize_t r;
+
+    memset (buffer, 0, sizeof buffer);
+
+    while (remaining > 0) {
+      n = remaining > sizeof buffer ? sizeof buffer : remaining;
+      r = write (fd, buffer, n);
+      if (r == -1) {
+        perrorf (g, _("%s: write"), filename);
+        close (fd);
+        unlink (filename);
+        return -1;
+      }
+      remaining -= r;
+    }
+#endif
+  }
+
+  if (close (fd) == -1) {
+    perrorf (g, _("%s: close"), filename);
+    unlink (filename);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */
+static int
+is_power_of_2 (unsigned v)
+{
+  return v && ((v & (v - 1)) == 0);
+}
+
+static int
+disk_create_qcow2 (guestfs_h *g, const char *orig_filename, int64_t size,
+                   const char *backingfile,
+                   const struct guestfs_disk_create_argv *optargs)
+{
+  CLEANUP_FREE char *filename = NULL;
+  const char *backingformat = NULL;
+  const char *preallocation = NULL;
+  const char *compat = NULL;
+  int clustersize = -1;
+  CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (optionsv);
+  CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
+  int r;
+
+  /* If the filename is something like "file:foo" then qemu-img will
+   * try to interpret that as "foo" in the file:/// protocol.  To
+   * avoid that, if the path is relative prefix it with "./" since
+   * qemu-img won't try to interpret such a path.
+   */
+  if (orig_filename[0] != '/')
+    filename = safe_asprintf (g, "./%s", orig_filename);
+  else
+    filename = safe_strdup (g, orig_filename);
+
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_BACKINGFORMAT_BITMASK) {
+    backingformat = optargs->backingformat;
+    if (STRNEQ (backingformat, "raw") && STRNEQ (backingformat, "qcow2")) {
+      error (g, _("invalid value for backingformat parameter '%s'"),
+             backingformat);
+      return -1;
+    }
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK) {
+    preallocation = optargs->preallocation;
+    if (STRNEQ (preallocation, "off") && STRNEQ (preallocation, "metadata")) {
+      error (g, _("invalid value for preallocation parameter '%s'"),
+             preallocation);
+      return -1;
+    }
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_COMPAT_BITMASK) {
+    compat = optargs->compat;
+    if (STRNEQ (compat, "0.10") && STRNEQ (compat, "1.1")) {
+      error (g, _("invalid value for compat parameter '%s'"), compat);
+      return -1;
+    }
+  }
+  if (optargs->bitmask & GUESTFS_DISK_CREATE_CLUSTERSIZE_BITMASK) {
+    clustersize = optargs->clustersize;
+    if (clustersize < 512 || clustersize > 2097152 ||
+        !is_power_of_2 ((unsigned) clustersize)) {
+      error (g, _("invalid value for clustersize parameter '%d'"),
+             clustersize);
+      return -1;
+    }
+  }
+
+  /* Assemble the qemu-img command line. */
+  guestfs___cmd_add_arg (cmd, "qemu-img");
+  guestfs___cmd_add_arg (cmd, "create");
+  guestfs___cmd_add_arg (cmd, "-f");
+  guestfs___cmd_add_arg (cmd, "qcow2");
+
+  /* -o parameter. */
+  if (backingfile) {
+    CLEANUP_FREE char *p = qemu_escape_param (g, backingfile);
+    guestfs___add_sprintf (g, &optionsv, "backing_file=%s", p);
+  }
+  if (backingformat)
+    guestfs___add_sprintf (g, &optionsv, "backing_fmt=%s", backingformat);
+  if (preallocation)
+    guestfs___add_sprintf (g, &optionsv, "preallocation=%s", preallocation);
+  if (compat)
+    guestfs___add_sprintf (g, &optionsv, "compat=%s", compat);
+  if (clustersize >= 0)
+    guestfs___add_sprintf (g, &optionsv, "cluster_size=%d", clustersize);
+  guestfs___end_stringsbuf (g, &optionsv);
+
+  if (optionsv.size > 1) {
+    CLEANUP_FREE char *options = guestfs___join_strings (",", optionsv.argv);
+    guestfs___cmd_add_arg (cmd, "-o");
+    guestfs___cmd_add_arg (cmd, options);
+  }
+
+  /* Complete the command line. */
+  guestfs___cmd_add_arg (cmd, filename);
+  if (size >= 0)
+    guestfs___cmd_add_arg_format (cmd, "%" PRIi64, size);
+
+  r = guestfs___cmd_run (cmd);
+  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
+    guestfs___external_command_failed (g, r, "qemu-img", orig_filename);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* XXX Duplicated in launch-direct.c. */
+static char *
+qemu_escape_param (guestfs_h *g, const char *param)
+{
+  size_t i, len = strlen (param);
+  char *p, *ret;
+
+  ret = p = safe_malloc (g, len*2 + 1); /* max length of escaped name*/
+  for (i = 0; i < len; ++i) {
+    *p++ = param[i];
+    if (param[i] == ',')
+      *p++ = ',';
+  }
+  *p = '\0';
+
+  return ret;
+}
diff --git a/tests/create/Makefile.am b/tests/create/Makefile.am
new file mode 100644
index 0000000..cdcecd2
--- /dev/null
+++ b/tests/create/Makefile.am
@@ -0,0 +1,27 @@
+# libguestfs
+# Copyright (C) 2014 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+TESTS = \
+	test-disk-create.sh
+
+TESTS_ENVIRONMENT = \
+	$(top_builddir)/run --test
+
+EXTRA_DIST = \
+	$(TESTS)
diff --git a/tests/create/test-disk-create.sh b/tests/create/test-disk-create.sh
new file mode 100755
index 0000000..967ceba
--- /dev/null
+++ b/tests/create/test-disk-create.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+# Copyright (C) 2014 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Test the disk-create API.
+
+export LANG=C
+
+set -e
+
+rm -f disk*.img file:*.img
+
+# XXX We should also test failure paths.
+
+../../fish/guestfish <<EOF
+  disk-create disk1.img  raw   256K
+  disk-create disk2.img  raw   256K preallocation:sparse
+  disk-create disk3.img  raw   256K preallocation:full
+  disk-create disk4.img  qcow2 256K
+  disk-create disk5.img  qcow2 256K preallocation:off
+  disk-create disk6.img  qcow2 256K preallocation:metadata
+  disk-create disk7.img  qcow2 256K compat:1.1
+  disk-create disk8.img  qcow2 256K clustersize:128K
+  disk-create disk9.img  qcow2 -1   backingfile:disk1.img compat:1.1
+  disk-create disk10.img qcow2 -1   backingfile:disk2.img backingformat:raw
+  disk-create disk11.img qcow2 -1   backingfile:disk4.img backingformat:qcow2
+
+  # Some annoying corner-cases in qemu-img.
+  disk-create disk:0.img qcow2 256K
+  disk-create file:0.img qcow2 256K
+  disk-create disk,0.img qcow2 256K
+  disk-create disk,,0.img qcow2 256K
+EOF
+
+output="$(../../fish/guestfish <<EOF
+  disk-format disk1.img
+  disk-format disk2.img
+  disk-format disk3.img
+  disk-format disk4.img
+  disk-format disk5.img
+  disk-format disk6.img
+  disk-format disk7.img
+  disk-format disk8.img
+  disk-format disk9.img
+  disk-format disk10.img
+  disk-format disk11.img
+  disk-format disk:0.img
+  disk-format file:0.img
+  disk-format disk,0.img
+  disk-format disk,,0.img
+
+  disk-has-backing-file disk1.img
+  disk-has-backing-file disk2.img
+  disk-has-backing-file disk3.img
+  disk-has-backing-file disk4.img
+  disk-has-backing-file disk5.img
+  disk-has-backing-file disk6.img
+  disk-has-backing-file disk7.img
+  disk-has-backing-file disk8.img
+  disk-has-backing-file disk9.img
+  disk-has-backing-file disk10.img
+  disk-has-backing-file disk11.img
+  disk-has-backing-file disk:0.img
+  disk-has-backing-file file:0.img
+  disk-has-backing-file disk,0.img
+  disk-has-backing-file disk,,0.img
+
+  disk-virtual-size disk1.img
+  disk-virtual-size disk2.img
+  disk-virtual-size disk3.img
+  disk-virtual-size disk4.img
+  disk-virtual-size disk5.img
+  disk-virtual-size disk6.img
+  disk-virtual-size disk7.img
+  disk-virtual-size disk8.img
+  disk-virtual-size disk9.img
+  disk-virtual-size disk10.img
+  disk-virtual-size disk11.img
+  disk-virtual-size disk:0.img
+  disk-virtual-size file:0.img
+  disk-virtual-size disk,0.img
+  disk-virtual-size disk,,0.img
+EOF
+)"
+
+if [ "$output" != "raw
+raw
+raw
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+qcow2
+false
+false
+false
+false
+false
+false
+false
+false
+true
+true
+true
+false
+false
+false
+false
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144
+262144" ]; then
+    echo "$0: unexpected output:"
+    echo "$output"
+    exit 1
+fi
+
+rm disk*.img file:*.img

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git



More information about the Pkg-libvirt-commits mailing list