commit a09a1635332165ab4d9510b7cab2f800edb23bf8
Author: Richard W.M. Jones <rjones(a)redhat.com>
Date: Fri Oct 31 22:29:33 2014 +0000
Fix: libvirt backend does not set RBD password (RHBZ#1159016).
...virt-Implement-drive-secrets-RHBZ-1159016.patch | 447 +++++++++
add-base64-to-gnulib.patch | 1041 ++++++++++++++++++++
libguestfs.spec | 17 +-
3 files changed, 1504 insertions(+), 1 deletions(-)
---
diff --git a/0001-launch-libvirt-Implement-drive-secrets-RHBZ-1159016.patch
b/0001-launch-libvirt-Implement-drive-secrets-RHBZ-1159016.patch
new file mode 100644
index 0000000..07d6ba4
--- /dev/null
+++ b/0001-launch-libvirt-Implement-drive-secrets-RHBZ-1159016.patch
@@ -0,0 +1,447 @@
+From 6d6644d52d8092dd4f4add859ebda06bea4b5b56 Mon Sep 17 00:00:00 2001
+From: "Richard W.M. Jones" <rjones(a)redhat.com>
+Date: Fri, 31 Oct 2014 11:02:33 +0000
+Subject: [PATCH] launch: libvirt: Implement drive secrets (RHBZ#1159016).
+
+Implement the GUESTFS_ADD_DRIVE_OPTS_SECRET argument of
+guestfs_add_drive_opts. For libvirt we have to save the secret in
+libvirtd first, get a UUID, and then pass the UUID back through the
+domain XML.
+---
+ bootstrap | 1 +
+ generator/actions.ml | 2 +-
+ m4/.gitignore | 7 +-
+ src/launch-libvirt.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 4 files changed, 275 insertions(+), 7 deletions(-)
+
+diff --git a/bootstrap b/bootstrap
+index 402ac4e..292f454 100755
+--- a/bootstrap
++++ b/bootstrap
+@@ -41,6 +41,7 @@ accept4
+ areadlink
+ areadlinkat
+ arpa_inet
++base64
+ byteswap
+ c-ctype
+ cloexec
+diff --git a/generator/actions.ml b/generator/actions.ml
+index ece3f88..83f4734 100644
+--- a/generator/actions.ml
++++ b/generator/actions.ml
+@@ -1494,7 +1494,7 @@ specify the remote username you want.
+ =item C<secret>
+
+ For the C<rbd> protocol only, this specifies the 'secret' to use when
+-connecting to the remote device.
++connecting to the remote device. It must be base64 encoded.
+
+ If not given, then a secret matching the given username will be looked up in the
+ default keychain locations, or if no username is given, then no authentication
+diff --git a/m4/.gitignore b/m4/.gitignore
+index 605cd26..b0f8d4b 100644
+--- a/m4/.gitignore
++++ b/m4/.gitignore
+@@ -5,6 +5,7 @@
+ /argmatch.m4
+ /arpa_inet_h.m4
+ /asm-underscore.m4
++/base64.m4
+ /btowc.m4
+ /byteswap.m4
+ /canonicalize-lgpl.m4
+@@ -104,6 +105,7 @@
+ /inttypes-pri.m4
+ /ioctl.m4
+ /i-ring.m4
++/isatty.m4
+ /isc-posix.m4
+ /largefile.m4
+ /lchown.m4
+@@ -166,6 +168,7 @@
+ /printf-posix.m4
+ /priv-set.m4
+ /progtest.m4
++/ptsname_r.m4
+ /putenv.m4
+ /quotearg.m4
+ /quote.m4
+@@ -236,6 +239,7 @@
+ /thread.m4
+ /time_h.m4
+ /timespec.m4
++/ttyname_r.m4
+ /uintmax_t.m4
+ /ulonglong.m4
+ /ungetc.m4
+@@ -271,6 +275,3 @@
+ /xstrtol.m4
+ /xvasprintf.m4
+ /yield.m4
+-/isatty.m4
+-/ptsname_r.m4
+-/ttyname_r.m4
+diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
+index 7206b33..26d3202 100644
+--- a/src/launch-libvirt.c
++++ b/src/launch-libvirt.c
+@@ -50,6 +50,7 @@
+ #endif
+
+ #include "glthread/lock.h"
++#include "base64.h"
+
+ #include "guestfs.h"
+ #include "guestfs-internal.h"
+@@ -97,6 +98,27 @@ xmlBufferDetach (xmlBufferPtr buf)
+ }
+ #endif
+
++#ifdef HAVE_ATTRIBUTE_CLEANUP
++#define CLEANUP_VIRSECRETFREE __attribute__((cleanup(cleanup_virSecretFree)))
++
++static void
++cleanup_virSecretFree (void *ptr)
++{
++ virSecretPtr secret_obj = * (virSecretPtr *) ptr;
++ if (secret_obj)
++ virSecretFree (secret_obj);
++}
++
++#else /* !HAVE_ATTRIBUTE_CLEANUP */
++#define CLEANUP_VIRSECRETFREE
++#endif
++
++/* List used to store a mapping of secret to libvirt secret UUID. */
++struct secret {
++ char *secret;
++ char uuid[VIR_UUID_STRING_BUFLEN];
++};
++
+ #define DOMAIN_NAME_LEN (8+16+1) /* "guestfs-" + random + \0 */
+
+ /* Per-handle data. */
+@@ -110,6 +132,8 @@ struct backend_libvirt_data {
+ char name[DOMAIN_NAME_LEN]; /* random name */
+ bool is_kvm; /* false = qemu, true = kvm (from capabilities)*/
+ unsigned long qemu_version; /* qemu version (from libvirt) */
++ struct secret *secrets; /* list of secrets */
++ size_t nr_secrets;
+ };
+
+ /* Parameters passed to construct_libvirt_xml and subfunctions. We
+@@ -130,6 +154,9 @@ struct libvirt_xml_params {
+ };
+
+ static int parse_capabilities (guestfs_h *g, const char *capabilities_xml, struct
backend_libvirt_data *data);
++static int add_secret (guestfs_h *g, virConnectPtr conn, struct backend_libvirt_data
*data, const struct drive *drv);
++static int find_secret (guestfs_h *g, const struct backend_libvirt_data *data, const
struct drive *drv, const char **type, const char **uuid);
++static int have_secret (guestfs_h *g, const struct backend_libvirt_data *data, const
struct drive *drv);
+ static xmlChar *construct_libvirt_xml (guestfs_h *g, const struct libvirt_xml_params
*params);
+ static void debug_appliance_permissions (guestfs_h *g);
+ static void debug_socket_permissions (guestfs_h *g);
+@@ -224,6 +251,8 @@ launch_libvirt (guestfs_h *g, void *datav, const char *libvirt_uri)
+ CLEANUP_FREE xmlChar *xml = NULL;
+ CLEANUP_FREE char *appliance = NULL;
+ struct sockaddr_un addr;
++ struct drive *drv;
++ size_t i;
+ int r;
+ uint32_t size;
+ CLEANUP_FREE void *buf = NULL;
+@@ -458,6 +487,14 @@ launch_libvirt (guestfs_h *g, void *datav, const char *libvirt_uri)
+ debug (g, "cannot find group 'qemu'");
+ }
+
++ /* Store any secrets in libvirtd, keeping a mapping from the secret
++ * to its UUID.
++ */
++ ITER_DRIVES (g, i, drv) {
++ if (add_secret (g, conn, data, drv) == -1)
++ goto cleanup;
++ }
++
+ /* Construct the libvirt XML. */
+ if (g->verbose)
+ guestfs___print_timestamped_message (g, "create libvirt XML");
+@@ -1272,6 +1309,8 @@ construct_libvirt_xml_disk (guestfs_h *g,
+ CLEANUP_FREE char *path = NULL;
+ int is_host_device;
+ CLEANUP_FREE char *format = NULL;
++ const char *type, *uuid;
++ int r;
+
+ /* XXX We probably could support this if we thought about it some more. */
+ if (drv->iface) {
+@@ -1383,9 +1422,15 @@ construct_libvirt_xml_disk (guestfs_h *g,
+ if (drv->src.username != NULL) {
+ start_element ("auth") {
+ attribute ("username", drv->src.username);
+- /* TODO: write the drive secret, after first storing it separately
+- * in libvirt
+- */
++ r = find_secret (g, data, drv, &type, &uuid);
++ if (r == -1)
++ return -1;
++ if (r == 1) {
++ start_element ("secret") {
++ attribute ("type", type);
++ attribute ("uuid", uuid);
++ } end_element ();
++ }
+ } end_element ();
+ }
+ break;
+@@ -1657,6 +1702,216 @@ construct_libvirt_xml_qemu_cmdline (guestfs_h *g,
+ }
+
+ static int
++construct_libvirt_xml_secret (guestfs_h *g,
++ const struct backend_libvirt_data *data,
++ const struct drive *drv,
++ xmlTextWriterPtr xo)
++{
++ start_element ("secret") {
++ attribute ("ephemeral", "yes");
++ attribute ("private", "yes");
++ start_element ("description") {
++ string_format ("guestfs secret associated with %s %s",
++ data->name, drv->src.u.path);
++ } end_element ();
++ } end_element ();
++
++ return 0;
++}
++
++/* If drv->src.secret != NULL, store the secret in libvirt, and save
++ * the UUID so we can retrieve it later. Also there is some slight
++ * variation depending on the protocol. See
++ *
http://libvirt.org/formatsecret.html
++ */
++static int
++add_secret (guestfs_h *g, virConnectPtr conn,
++ struct backend_libvirt_data *data, const struct drive *drv)
++{
++ CLEANUP_XMLBUFFERFREE xmlBufferPtr xb = NULL;
++ xmlOutputBufferPtr ob;
++ CLEANUP_XMLFREETEXTWRITER xmlTextWriterPtr xo = NULL;
++ CLEANUP_FREE xmlChar *xml = NULL;
++ CLEANUP_VIRSECRETFREE virSecretPtr secret_obj = NULL;
++ const char *secret = drv->src.secret;
++ CLEANUP_FREE unsigned char *secret_raw = NULL;
++ size_t secret_raw_len = 0;
++ size_t i;
++
++ if (secret == NULL)
++ return 0;
++
++ /* If it was already stored, don't create another secret. */
++ if (have_secret (g, data, drv))
++ return 0;
++
++ /* Create the XML for the secret. */
++ xb = xmlBufferCreate ();
++ if (xb == NULL) {
++ perrorf (g, "xmlBufferCreate");
++ return -1;
++ }
++ ob = xmlOutputBufferCreateBuffer (xb, NULL);
++ if (ob == NULL) {
++ perrorf (g, "xmlOutputBufferCreateBuffer");
++ return -1;
++ }
++ xo = xmlNewTextWriter (ob);
++ if (xo == NULL) {
++ perrorf (g, "xmlNewTextWriter");
++ return -1;
++ }
++
++ if (xmlTextWriterSetIndent (xo, 1) == -1 ||
++ xmlTextWriterSetIndentString (xo, BAD_CAST " ") == -1) {
++ perrorf (g, "could not set XML indent");
++ return -1;
++ }
++ if (xmlTextWriterStartDocument (xo, NULL, NULL, NULL) == -1) {
++ perrorf (g, "xmlTextWriterStartDocument");
++ return -1;
++ }
++
++ if (construct_libvirt_xml_secret (g, data, drv, xo) == -1)
++ return -1;
++
++ if (xmlTextWriterEndDocument (xo) == -1) {
++ perrorf (g, "xmlTextWriterEndDocument");
++ return -1;
++ }
++ xml = xmlBufferDetach (xb);
++ if (xml == NULL) {
++ perrorf (g, "xmlBufferDetach");
++ return -1;
++ }
++
++ debug (g, "libvirt secret XML:\n%s", xml);
++
++ /* Pass the XML to libvirt. */
++ secret_obj = virSecretDefineXML (conn, (const char *) xml, 0);
++ if (secret_obj == NULL) {
++ libvirt_error (g, _("could not define libvirt secret"));
++ return -1;
++ }
++
++ /* For Ceph, we have to base64 decode the secret. For others, we
++ * currently just pass the secret straight through.
++ */
++ switch (drv->src.protocol) {
++ case drive_protocol_rbd:
++ if (!base64_decode_alloc (secret, strlen (secret),
++ (char **) &secret_raw, &secret_raw_len)) {
++ error (g, _("rbd protocol secret must be base64 encoded"));
++ return -1;
++ }
++ if (secret_raw == NULL) {
++ error (g, _("base64_decode_alloc: %m"));
++ return -1;
++ }
++ break;
++ case drive_protocol_file:
++ case drive_protocol_ftp:
++ case drive_protocol_ftps:
++ case drive_protocol_gluster:
++ case drive_protocol_http:
++ case drive_protocol_https:
++ case drive_protocol_iscsi:
++ case drive_protocol_nbd:
++ case drive_protocol_sheepdog:
++ case drive_protocol_ssh:
++ case drive_protocol_tftp:
++ secret_raw = (unsigned char *) safe_strdup (g, secret);
++ secret_raw_len = strlen (secret);
++ }
++
++ /* Set the secret. */
++ if (virSecretSetValue (secret_obj, secret_raw, secret_raw_len, 0) == -1) {
++ libvirt_error (g, _("could not set libvirt secret value"));
++ return -1;
++ }
++
++ /* Get back the UUID and save it in the private data. */
++ i = data->nr_secrets;
++ data->nr_secrets++;
++ data->secrets =
++ safe_realloc (g, data->secrets, sizeof (struct secret) * data->nr_secrets);
++
++ data->secrets[i].secret = safe_strdup (g, secret);
++
++ if (virSecretGetUUIDString (secret_obj, data->secrets[i].uuid) == -1) {
++ libvirt_error (g, _("could not get UUID from libvirt secret"));
++ return -1;
++ }
++
++ return 0;
++}
++
++static int
++have_secret (guestfs_h *g,
++ const struct backend_libvirt_data *data, const struct drive *drv)
++{
++ size_t i;
++
++ if (drv->src.secret == NULL)
++ return 0;
++
++ for (i = 0; i < data->nr_secrets; ++i) {
++ if (STREQ (data->secrets[i].secret, drv->src.secret))
++ return 1;
++ }
++
++ return 0;
++}
++
++/* Find a secret previously stored in libvirt. Returns the
++ * <secret type=... uuid=...> attributes. This function returns -1
++ * if there was an error, 0 if there is no secret, and 1 if the
++ * secret was found and returned.
++ */
++static int
++find_secret (guestfs_h *g,
++ const struct backend_libvirt_data *data, const struct drive *drv,
++ const char **type, const char **uuid)
++{
++ size_t i;
++
++ if (drv->src.secret == NULL)
++ return 0;
++
++ for (i = 0; i < data->nr_secrets; ++i) {
++ if (STREQ (data->secrets[i].secret, drv->src.secret)) {
++ *uuid = data->secrets[i].uuid;
++
++ *type = "volume";
++
++ switch (drv->src.protocol) {
++ case drive_protocol_rbd:
++ *type = "ceph";
++ break;
++ case drive_protocol_iscsi:
++ *type = "iscsi";
++ break;
++ case drive_protocol_file:
++ case drive_protocol_ftp:
++ case drive_protocol_ftps:
++ case drive_protocol_gluster:
++ case drive_protocol_http:
++ case drive_protocol_https:
++ case drive_protocol_nbd:
++ case drive_protocol_sheepdog:
++ case drive_protocol_ssh:
++ case drive_protocol_tftp:
++ /* set to a default value above */ ;
++ }
++
++ return 1;
++ }
++ }
++
++ return 0;
++}
++
++static int
+ is_blk (const char *path)
+ {
+ struct stat statbuf;
+@@ -1678,6 +1933,7 @@ shutdown_libvirt (guestfs_h *g, void *datav, int check_for_errors)
+ struct backend_libvirt_data *data = datav;
+ virConnectPtr conn = data->conn;
+ virDomainPtr dom = data->dom;
++ size_t i;
+ int ret = 0;
+ int flags;
+
+@@ -1710,6 +1966,12 @@ shutdown_libvirt (guestfs_h *g, void *datav, int
check_for_errors)
+ free (data->network_bridge);
+ data->network_bridge = NULL;
+
++ for (i = 0; i < data->nr_secrets; ++i)
++ free (data->secrets[i].secret);
++ free (data->secrets);
++ data->secrets = NULL;
++ data->nr_secrets = 0;
++
+ return ret;
+ }
+
+@@ -1814,6 +2076,10 @@ hot_add_drive_libvirt (guestfs_h *g, void *datav,
+ return -1;
+ }
+
++ /* If the drive has an associated secret, store it in libvirt. */
++ if (add_secret (g, conn, data, drv) == -1)
++ return -1;
++
+ /* Create the XML for the new disk. */
+ xml = construct_libvirt_xml_hot_add_disk (g, data, drv, drv_index);
+ if (xml == NULL)
+--
+2.0.4
+
diff --git a/add-base64-to-gnulib.patch b/add-base64-to-gnulib.patch
new file mode 100644
index 0000000..1e894de
--- /dev/null
+++ b/add-base64-to-gnulib.patch
@@ -0,0 +1,1041 @@
+diff -urN libguestfs-1.28.2.old/aclocal.m4 libguestfs-1.28.2/aclocal.m4
+--- libguestfs-1.28.2.old/aclocal.m4 2014-10-27 14:46:56.000000000 +0000
++++ libguestfs-1.28.2/aclocal.m4 2014-10-31 22:11:26.000000000 +0000
+@@ -1367,6 +1367,7 @@
+ m4_include([m4/accept4.m4])
+ m4_include([m4/alloca.m4])
+ m4_include([m4/arpa_inet_h.m4])
++m4_include([m4/base64.m4])
+ m4_include([m4/btowc.m4])
+ m4_include([m4/byteswap.m4])
+ m4_include([m4/chdir-long.m4])
+diff -urN libguestfs-1.28.2.old/gnulib/lib/base64.c
libguestfs-1.28.2/gnulib/lib/base64.c
+--- libguestfs-1.28.2.old/gnulib/lib/base64.c 1970-01-01 01:00:00.000000000 +0100
++++ libguestfs-1.28.2/gnulib/lib/base64.c 2014-10-31 21:57:50.000000000 +0000
+@@ -0,0 +1,605 @@
++/* base64.c -- Encode binary data using printable characters.
++ Copyright (C) 1999-2001, 2004-2006, 2009-2014 Free Software Foundation, 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 3, 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, see <
http://www.gnu.org/licenses/>. */
++
++/* Written by Simon Josefsson. Partially adapted from GNU MailUtils
++ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
++ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
++ *
++ * See also RFC 4648 <
http://www.ietf.org/rfc/rfc4648.txt>.
++ *
++ * Be careful with error checking. Here is how you would typically
++ * use these functions:
++ *
++ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
++ * if (!ok)
++ * FAIL: input was not valid base64
++ * if (out == NULL)
++ * FAIL: memory allocation error
++ * OK: data in OUT/OUTLEN
++ *
++ * size_t outlen = base64_encode_alloc (in, inlen, &out);
++ * if (out == NULL && outlen == 0 && inlen != 0)
++ * FAIL: input too long
++ * if (out == NULL)
++ * FAIL: memory allocation error
++ * OK: data in OUT/OUTLEN.
++ *
++ */
++
++#include <config.h>
++
++/* Get prototype. */
++#include "base64.h"
++
++/* Get malloc. */
++#include <stdlib.h>
++
++/* Get UCHAR_MAX. */
++#include <limits.h>
++
++#include <string.h>
++
++/* C89 compliant way to cast 'char' to 'unsigned char'. */
++static unsigned char
++to_uchar (char ch)
++{
++ return ch;
++}
++
++static const char b64c[64] =
++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
++
++/* Base64 encode IN array of size INLEN into OUT array. OUT needs
++ to be of length >= BASE64_LENGTH(INLEN), and INLEN needs to be
++ a multiple of 3. */
++static void
++base64_encode_fast (const char *restrict in, size_t inlen, char *restrict out)
++{
++ while (inlen)
++ {
++ *out++ = b64c[to_uchar (in[0]) >> 2];
++ *out++ = b64c[((to_uchar (in[0]) << 4) + (to_uchar (in[1]) >> 4))
& 0x3f];
++ *out++ = b64c[((to_uchar (in[1]) << 2) + (to_uchar (in[2]) >> 6))
& 0x3f];
++ *out++ = b64c[to_uchar (in[2]) & 0x3f];
++
++ inlen -= 3;
++ in += 3;
++ }
++}
++
++/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
++ If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
++ possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
++ terminate the output buffer. */
++void
++base64_encode (const char *restrict in, size_t inlen,
++ char *restrict out, size_t outlen)
++{
++ /* Note this outlen constraint can be enforced at compile time.
++ I.E. that the output buffer is exactly large enough to hold
++ the encoded inlen bytes. The inlen constraints (of corresponding
++ to outlen, and being a multiple of 3) can change at runtime
++ at the end of input. However the common case when reading
++ large inputs is to have both constraints satisfied, so we depend
++ on both in base_encode_fast(). */
++ if (outlen % 4 == 0 && inlen == outlen / 4 * 3)
++ {
++ base64_encode_fast (in, inlen, out);
++ return;
++ }
++
++ while (inlen && outlen)
++ {
++ *out++ = b64c[to_uchar (in[0]) >> 2];
++ if (!--outlen)
++ break;
++ *out++ = b64c[((to_uchar (in[0]) << 4)
++ + (--inlen ? to_uchar (in[1]) >> 4 : 0))
++ & 0x3f];
++ if (!--outlen)
++ break;
++ *out++ =
++ (inlen
++ ? b64c[((to_uchar (in[1]) << 2)
++ + (--inlen ? to_uchar (in[2]) >> 6 : 0))
++ & 0x3f]
++ : '=');
++ if (!--outlen)
++ break;
++ *out++ = inlen ? b64c[to_uchar (in[2]) & 0x3f] : '=';
++ if (!--outlen)
++ break;
++ if (inlen)
++ inlen--;
++ if (inlen)
++ in += 3;
++ }
++
++ if (outlen)
++ *out = '\0';
++}
++
++/* Allocate a buffer and store zero terminated base64 encoded data
++ from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
++ the length of the encoded data, excluding the terminating zero. On
++ return, the OUT variable will hold a pointer to newly allocated
++ memory that must be deallocated by the caller. If output string
++ length would overflow, 0 is returned and OUT is set to NULL. If
++ memory allocation failed, OUT is set to NULL, and the return value
++ indicates length of the requested memory block, i.e.,
++ BASE64_LENGTH(inlen) + 1. */
++size_t
++base64_encode_alloc (const char *in, size_t inlen, char **out)
++{
++ size_t outlen = 1 + BASE64_LENGTH (inlen);
++
++ /* Check for overflow in outlen computation.
++ *
++ * If there is no overflow, outlen >= inlen.
++ *
++ * If the operation (inlen + 2) overflows then it yields at most +1, so
++ * outlen is 0.
++ *
++ * If the multiplication overflows, we lose at least half of the
++ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
++ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
++ * (inlen > 4).
++ */
++ if (inlen > outlen)
++ {
++ *out = NULL;
++ return 0;
++ }
++
++ *out = malloc (outlen);
++ if (!*out)
++ return outlen;
++
++ base64_encode (in, inlen, *out, outlen);
++
++ return outlen - 1;
++}
++
++/* With this approach this file works independent of the charset used
++ (think EBCDIC). However, it does assume that the characters in the
++ Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX
++ 1003.1-2001 require that char and unsigned char are 8-bit
++ quantities, though, taking care of that problem. But this may be a
++ potential problem on non-POSIX C99 platforms.
++
++ IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use
"_"
++ as the formal parameter rather than "x". */
++#define B64(_) \
++ ((_) == 'A' ? 0 \
++ : (_) == 'B' ? 1 \
++ : (_) == 'C' ? 2 \
++ : (_) == 'D' ? 3 \
++ : (_) == 'E' ? 4 \
++ : (_) == 'F' ? 5 \
++ : (_) == 'G' ? 6 \
++ : (_) == 'H' ? 7 \
++ : (_) == 'I' ? 8 \
++ : (_) == 'J' ? 9 \
++ : (_) == 'K' ? 10 \
++ : (_) == 'L' ? 11 \
++ : (_) == 'M' ? 12 \
++ : (_) == 'N' ? 13 \
++ : (_) == 'O' ? 14 \
++ : (_) == 'P' ? 15 \
++ : (_) == 'Q' ? 16 \
++ : (_) == 'R' ? 17 \
++ : (_) == 'S' ? 18 \
++ : (_) == 'T' ? 19 \
++ : (_) == 'U' ? 20 \
++ : (_) == 'V' ? 21 \
++ : (_) == 'W' ? 22 \
++ : (_) == 'X' ? 23 \
++ : (_) == 'Y' ? 24 \
++ : (_) == 'Z' ? 25 \
++ : (_) == 'a' ? 26 \
++ : (_) == 'b' ? 27 \
++ : (_) == 'c' ? 28 \
++ : (_) == 'd' ? 29 \
++ : (_) == 'e' ? 30 \
++ : (_) == 'f' ? 31 \
++ : (_) == 'g' ? 32 \
++ : (_) == 'h' ? 33 \
++ : (_) == 'i' ? 34 \
++ : (_) == 'j' ? 35 \
++ : (_) == 'k' ? 36 \
++ : (_) == 'l' ? 37 \
++ : (_) == 'm' ? 38 \
++ : (_) == 'n' ? 39 \
++ : (_) == 'o' ? 40 \
++ : (_) == 'p' ? 41 \
++ : (_) == 'q' ? 42 \
++ : (_) == 'r' ? 43 \
++ : (_) == 's' ? 44 \
++ : (_) == 't' ? 45 \
++ : (_) == 'u' ? 46 \
++ : (_) == 'v' ? 47 \
++ : (_) == 'w' ? 48 \
++ : (_) == 'x' ? 49 \
++ : (_) == 'y' ? 50 \
++ : (_) == 'z' ? 51 \
++ : (_) == '0' ? 52 \
++ : (_) == '1' ? 53 \
++ : (_) == '2' ? 54 \
++ : (_) == '3' ? 55 \
++ : (_) == '4' ? 56 \
++ : (_) == '5' ? 57 \
++ : (_) == '6' ? 58 \
++ : (_) == '7' ? 59 \
++ : (_) == '8' ? 60 \
++ : (_) == '9' ? 61 \
++ : (_) == '+' ? 62 \
++ : (_) == '/' ? 63 \
++ : -1)
++
++static const signed char b64[0x100] = {
++ B64 (0), B64 (1), B64 (2), B64 (3),
++ B64 (4), B64 (5), B64 (6), B64 (7),
++ B64 (8), B64 (9), B64 (10), B64 (11),
++ B64 (12), B64 (13), B64 (14), B64 (15),
++ B64 (16), B64 (17), B64 (18), B64 (19),
++ B64 (20), B64 (21), B64 (22), B64 (23),
++ B64 (24), B64 (25), B64 (26), B64 (27),
++ B64 (28), B64 (29), B64 (30), B64 (31),
++ B64 (32), B64 (33), B64 (34), B64 (35),
++ B64 (36), B64 (37), B64 (38), B64 (39),
++ B64 (40), B64 (41), B64 (42), B64 (43),
++ B64 (44), B64 (45), B64 (46), B64 (47),
++ B64 (48), B64 (49), B64 (50), B64 (51),
++ B64 (52), B64 (53), B64 (54), B64 (55),
++ B64 (56), B64 (57), B64 (58), B64 (59),
++ B64 (60), B64 (61), B64 (62), B64 (63),
++ B64 (64), B64 (65), B64 (66), B64 (67),
++ B64 (68), B64 (69), B64 (70), B64 (71),
++ B64 (72), B64 (73), B64 (74), B64 (75),
++ B64 (76), B64 (77), B64 (78), B64 (79),
++ B64 (80), B64 (81), B64 (82), B64 (83),
++ B64 (84), B64 (85), B64 (86), B64 (87),
++ B64 (88), B64 (89), B64 (90), B64 (91),
++ B64 (92), B64 (93), B64 (94), B64 (95),
++ B64 (96), B64 (97), B64 (98), B64 (99),
++ B64 (100), B64 (101), B64 (102), B64 (103),
++ B64 (104), B64 (105), B64 (106), B64 (107),
++ B64 (108), B64 (109), B64 (110), B64 (111),
++ B64 (112), B64 (113), B64 (114), B64 (115),
++ B64 (116), B64 (117), B64 (118), B64 (119),
++ B64 (120), B64 (121), B64 (122), B64 (123),
++ B64 (124), B64 (125), B64 (126), B64 (127),
++ B64 (128), B64 (129), B64 (130), B64 (131),
++ B64 (132), B64 (133), B64 (134), B64 (135),
++ B64 (136), B64 (137), B64 (138), B64 (139),
++ B64 (140), B64 (141), B64 (142), B64 (143),
++ B64 (144), B64 (145), B64 (146), B64 (147),
++ B64 (148), B64 (149), B64 (150), B64 (151),
++ B64 (152), B64 (153), B64 (154), B64 (155),
++ B64 (156), B64 (157), B64 (158), B64 (159),
++ B64 (160), B64 (161), B64 (162), B64 (163),
++ B64 (164), B64 (165), B64 (166), B64 (167),
++ B64 (168), B64 (169), B64 (170), B64 (171),
++ B64 (172), B64 (173), B64 (174), B64 (175),
++ B64 (176), B64 (177), B64 (178), B64 (179),
++ B64 (180), B64 (181), B64 (182), B64 (183),
++ B64 (184), B64 (185), B64 (186), B64 (187),
++ B64 (188), B64 (189), B64 (190), B64 (191),
++ B64 (192), B64 (193), B64 (194), B64 (195),
++ B64 (196), B64 (197), B64 (198), B64 (199),
++ B64 (200), B64 (201), B64 (202), B64 (203),
++ B64 (204), B64 (205), B64 (206), B64 (207),
++ B64 (208), B64 (209), B64 (210), B64 (211),
++ B64 (212), B64 (213), B64 (214), B64 (215),
++ B64 (216), B64 (217), B64 (218), B64 (219),
++ B64 (220), B64 (221), B64 (222), B64 (223),
++ B64 (224), B64 (225), B64 (226), B64 (227),
++ B64 (228), B64 (229), B64 (230), B64 (231),
++ B64 (232), B64 (233), B64 (234), B64 (235),
++ B64 (236), B64 (237), B64 (238), B64 (239),
++ B64 (240), B64 (241), B64 (242), B64 (243),
++ B64 (244), B64 (245), B64 (246), B64 (247),
++ B64 (248), B64 (249), B64 (250), B64 (251),
++ B64 (252), B64 (253), B64 (254), B64 (255)
++};
++
++#if UCHAR_MAX == 255
++# define uchar_in_range(c) true
++#else
++# define uchar_in_range(c) ((c) <= 255)
++#endif
++
++/* Return true if CH is a character from the Base64 alphabet, and
++ false otherwise. Note that '=' is padding and not considered to be
++ part of the alphabet. */
++bool
++isbase64 (char ch)
++{
++ return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
++}
++
++/* Initialize decode-context buffer, CTX. */
++void
++base64_decode_ctx_init (struct base64_decode_context *ctx)
++{
++ ctx->i = 0;
++}
++
++/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
++ none of those four is a newline, then return *IN. Otherwise, copy up to
++ 4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
++ index CTX->i and setting CTX->i to reflect the number of bytes copied,
++ and return CTX->buf. In either case, advance *IN to point to the byte
++ after the last one processed, and set *N_NON_NEWLINE to the number of
++ verified non-newline bytes accessible through the returned pointer. */
++static char *
++get_4 (struct base64_decode_context *ctx,
++ char const *restrict *in, char const *restrict in_end,
++ size_t *n_non_newline)
++{
++ if (ctx->i == 4)
++ ctx->i = 0;
++
++ if (ctx->i == 0)
++ {
++ char const *t = *in;
++ if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
++ {
++ /* This is the common case: no newline. */
++ *in += 4;
++ *n_non_newline = 4;
++ return (char *) t;
++ }
++ }
++
++ {
++ /* Copy non-newline bytes into BUF. */
++ char const *p = *in;
++ while (p < in_end)
++ {
++ char c = *p++;
++ if (c != '\n')
++ {
++ ctx->buf[ctx->i++] = c;
++ if (ctx->i == 4)
++ break;
++ }
++ }
++
++ *in = p;
++ *n_non_newline = ctx->i;
++ return ctx->buf;
++ }
++}
++
++#define return_false \
++ do \
++ { \
++ *outp = out; \
++ return false; \
++ } \
++ while (false)
++
++/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
++ into the output buffer, *OUT, of size *OUTLEN bytes. Return true if
++ decoding is successful, false otherwise. If *OUTLEN is too small,
++ as many bytes as possible are written to *OUT. On return, advance
++ *OUT to point to the byte after the last one written, and decrement
++ *OUTLEN to reflect the number of bytes remaining in *OUT. */
++static bool
++decode_4 (char const *restrict in, size_t inlen,
++ char *restrict *outp, size_t *outleft)
++{
++ char *out = *outp;
++ if (inlen < 2)
++ return false;
++
++ if (!isbase64 (in[0]) || !isbase64 (in[1]))
++ return false;
++
++ if (*outleft)
++ {
++ *out++ = ((b64[to_uchar (in[0])] << 2)
++ | (b64[to_uchar (in[1])] >> 4));
++ --*outleft;
++ }
++
++ if (inlen == 2)
++ return_false;
++
++ if (in[2] == '=')
++ {
++ if (inlen != 4)
++ return_false;
++
++ if (in[3] != '=')
++ return_false;
++ }
++ else
++ {
++ if (!isbase64 (in[2]))
++ return_false;
++
++ if (*outleft)
++ {
++ *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
++ | (b64[to_uchar (in[2])] >> 2));
++ --*outleft;
++ }
++
++ if (inlen == 3)
++ return_false;
++
++ if (in[3] == '=')
++ {
++ if (inlen != 4)
++ return_false;
++ }
++ else
++ {
++ if (!isbase64 (in[3]))
++ return_false;
++
++ if (*outleft)
++ {
++ *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
++ | b64[to_uchar (in[3])]);
++ --*outleft;
++ }
++ }
++ }
++
++ *outp = out;
++ return true;
++}
++
++/* Decode base64-encoded input array IN of length INLEN to output array
++ OUT that can hold *OUTLEN bytes. The input data may be interspersed
++ with newlines. Return true if decoding was successful, i.e. if the
++ input was valid base64 data, false otherwise. If *OUTLEN is too
++ small, as many bytes as possible will be written to OUT. On return,
++ *OUTLEN holds the length of decoded bytes in OUT. Note that as soon
++ as any non-alphabet, non-newline character is encountered, decoding
++ is stopped and false is returned. If INLEN is zero, then process
++ only whatever data is stored in CTX.
++
++ Initially, CTX must have been initialized via base64_decode_ctx_init.
++ Subsequent calls to this function must reuse whatever state is recorded
++ in that buffer. It is necessary for when a quadruple of base64 input
++ bytes spans two input buffers.
++
++ If CTX is NULL then newlines are treated as garbage and the input
++ buffer is processed as a unit. */
++
++bool
++base64_decode_ctx (struct base64_decode_context *ctx,
++ const char *restrict in, size_t inlen,
++ char *restrict out, size_t *outlen)
++{
++ size_t outleft = *outlen;
++ bool ignore_newlines = ctx != NULL;
++ bool flush_ctx = false;
++ unsigned int ctx_i = 0;
++
++ if (ignore_newlines)
++ {
++ ctx_i = ctx->i;
++ flush_ctx = inlen == 0;
++ }
++
++
++ while (true)
++ {
++ size_t outleft_save = outleft;
++ if (ctx_i == 0 && !flush_ctx)
++ {
++ while (true)
++ {
++ /* Save a copy of outleft, in case we need to re-parse this
++ block of four bytes. */
++ outleft_save = outleft;
++ if (!decode_4 (in, inlen, &out, &outleft))
++ break;
++
++ in += 4;
++ inlen -= 4;
++ }
++ }
++
++ if (inlen == 0 && !flush_ctx)
++ break;
++
++ /* Handle the common case of 72-byte wrapped lines.
++ This also handles any other multiple-of-4-byte wrapping. */
++ if (inlen && *in == '\n' && ignore_newlines)
++ {
++ ++in;
++ --inlen;
++ continue;
++ }
++
++ /* Restore OUT and OUTLEFT. */
++ out -= outleft_save - outleft;
++ outleft = outleft_save;
++
++ {
++ char const *in_end = in + inlen;
++ char const *non_nl;
++
++ if (ignore_newlines)
++ non_nl = get_4 (ctx, &in, in_end, &inlen);
++ else
++ non_nl = in; /* Might have nl in this case. */
++
++ /* If the input is empty or consists solely of newlines (0 non-newlines),
++ then we're done. Likewise if there are fewer than 4 bytes when not
++ flushing context and not treating newlines as garbage. */
++ if (inlen == 0 || (inlen < 4 && !flush_ctx &&
ignore_newlines))
++ {
++ inlen = 0;
++ break;
++ }
++ if (!decode_4 (non_nl, inlen, &out, &outleft))
++ break;
++
++ inlen = in_end - in;
++ }
++ }
++
++ *outlen -= outleft;
++
++ return inlen == 0;
++}
++
++/* Allocate an output buffer in *OUT, and decode the base64 encoded
++ data stored in IN of size INLEN to the *OUT buffer. On return, the
++ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
++ if the caller is not interested in the decoded length. *OUT may be
++ NULL to indicate an out of memory error, in which case *OUTLEN
++ contains the size of the memory block needed. The function returns
++ true on successful decoding and memory allocation errors. (Use the
++ *OUT and *OUTLEN parameters to differentiate between successful
++ decoding and memory error.) The function returns false if the
++ input was invalid, in which case *OUT is NULL and *OUTLEN is
++ undefined. */
++bool
++base64_decode_alloc_ctx (struct base64_decode_context *ctx,
++ const char *in, size_t inlen, char **out,
++ size_t *outlen)
++{
++ /* This may allocate a few bytes too many, depending on input,
++ but it's not worth the extra CPU time to compute the exact size.
++ The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
++ input ends with "=" and minus another 1 if the input ends with
"==".
++ Dividing before multiplying avoids the possibility of overflow. */
++ size_t needlen = 3 * (inlen / 4) + 3;
++
++ *out = malloc (needlen);
++ if (!*out)
++ return true;
++
++ if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
++ {
++ free (*out);
++ *out = NULL;
++ return false;
++ }
++
++ if (outlen)
++ *outlen = needlen;
++
++ return true;
++}
+diff -urN libguestfs-1.28.2.old/gnulib/lib/base64.h
libguestfs-1.28.2/gnulib/lib/base64.h
+--- libguestfs-1.28.2.old/gnulib/lib/base64.h 1970-01-01 01:00:00.000000000 +0100
++++ libguestfs-1.28.2/gnulib/lib/base64.h 2014-10-31 21:57:50.000000000 +0000
+@@ -0,0 +1,68 @@
++/* base64.h -- Encode binary data using printable characters.
++ Copyright (C) 2004-2006, 2009-2014 Free Software Foundation, Inc.
++ Written by Simon Josefsson.
++
++ 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 3, 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, see <
http://www.gnu.org/licenses/>. */
++
++#ifndef BASE64_H
++# define BASE64_H
++
++/* Get size_t. */
++# include <stddef.h>
++
++/* Get bool. */
++# include <stdbool.h>
++
++# ifdef __cplusplus
++extern "C" {
++# endif
++
++/* This uses that the expression (n+(k-1))/k means the smallest
++ integer >= n/k, i.e., the ceiling of n/k. */
++# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
++
++struct base64_decode_context
++{
++ unsigned int i;
++ char buf[4];
++};
++
++extern bool isbase64 (char ch) _GL_ATTRIBUTE_CONST;
++
++extern void base64_encode (const char *restrict in, size_t inlen,
++ char *restrict out, size_t outlen);
++
++extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
++
++extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
++
++extern bool base64_decode_ctx (struct base64_decode_context *ctx,
++ const char *restrict in, size_t inlen,
++ char *restrict out, size_t *outlen);
++
++extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
++ const char *in, size_t inlen,
++ char **out, size_t *outlen);
++
++#define base64_decode(in, inlen, out, outlen) \
++ base64_decode_ctx (NULL, in, inlen, out, outlen)
++
++#define base64_decode_alloc(in, inlen, out, outlen) \
++ base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
++
++# ifdef __cplusplus
++}
++# endif
++
++#endif /* BASE64_H */
+diff -urN libguestfs-1.28.2.old/gnulib/lib/Makefile.am
libguestfs-1.28.2/gnulib/lib/Makefile.am
+--- libguestfs-1.28.2.old/gnulib/lib/Makefile.am 2014-10-27 14:46:38.000000000 +0000
++++ libguestfs-1.28.2/gnulib/lib/Makefile.am 2014-10-31 21:59:06.000000000 +0000
+@@ -21,7 +21,7 @@
+ # the same distribution terms as the rest of that program.
+ #
+ # Generated by gnulib-tool.
+-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib
--m4-base=m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests
--avoid=dummy --avoid=getlogin_r-tests --no-conditional-dependencies --libtool
--macro-prefix=gl accept4 areadlink areadlinkat arpa_inet byteswap c-ctype cloexec
closeout connect dup3 error filevercmp fstatat fsusage fts full-read full-write futimens
getaddrinfo getline gitlog-to-changelog glob gnu-make gnumakefile hash hash-pjw human
iconv ignore-value lock maintainer-makefile manywarnings memmem mkdtemp mkstemps netdb
netinet_in openat perror pipe2 pread ptsname_r read-file readlink select setenv sleep
socket stat-time strchrnul strerror strndup symlinkat sys_select sys_wait vasprintf
vc-list-files warnings xalloc xalloc-die xstrtol xstrtoll xvasprintf
++# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=gnulib/lib
--m4-base=m4 --doc-base=doc --tests-base=gnulib/tests --aux-dir=build-aux --with-tests
--avoid=dummy --avoid=getlogin_r-tests --no-conditional-dependencies --libtool
--macro-prefix=gl accept4 areadlink areadlinkat arpa_inet base64 byteswap c-ctype cloexec
closeout connect dup3 error filevercmp fstatat fsusage fts full-read full-write futimens
getaddrinfo getline gitlog-to-changelog glob gnu-make gnumakefile hash hash-pjw human
iconv ignore-value lock maintainer-makefile manywarnings memmem mkdtemp mkstemps netdb
netinet_in openat perror pipe2 pread ptsname_r read-file readlink select setenv sleep
socket stat-time strchrnul strerror strndup symlinkat sys_select sys_wait vasprintf
vc-list-files warnings xalloc xalloc-die xstrtol xstrtoll xvasprintf
+
+ AUTOMAKE_OPTIONS = 1.9.6 gnits subdir-objects
+
+@@ -197,6 +197,12 @@
+
+ ## end gnulib module at-internal
+
++## begin gnulib module base64
++
++libgnu_la_SOURCES += base64.h base64.c
++
++## end gnulib module base64
++
+ ## begin gnulib module binary-io
+
+ libgnu_la_SOURCES += binary-io.h binary-io.c
+diff -urN libguestfs-1.28.2.old/gnulib/tests/Makefile.am
libguestfs-1.28.2/gnulib/tests/Makefile.am
+--- libguestfs-1.28.2.old/gnulib/tests/Makefile.am 2014-10-27 14:46:42.000000000 +0000
++++ libguestfs-1.28.2/gnulib/tests/Makefile.am 2014-10-31 22:00:05.000000000 +0000
+@@ -120,6 +120,14 @@
+
+ ## end gnulib module arpa_inet-tests
+
++## begin gnulib module base64-tests
++
++TESTS += test-base64
++check_PROGRAMS += test-base64
++EXTRA_DIST += test-base64.c macros.h
++
++## end gnulib module base64-tests
++
+ ## begin gnulib module binary-io-tests
+
+ TESTS += test-binary-io.sh
+diff -urN libguestfs-1.28.2.old/gnulib/tests/test-base64.c
libguestfs-1.28.2/gnulib/tests/test-base64.c
+--- libguestfs-1.28.2.old/gnulib/tests/test-base64.c 1970-01-01 01:00:00.000000000 +0100
++++ libguestfs-1.28.2/gnulib/tests/test-base64.c 2014-10-31 21:57:50.000000000 +0000
+@@ -0,0 +1,238 @@
++/* Self tests for base64.
++ Copyright (C) 2004, 2008-2014 Free Software Foundation, Inc.
++ Written by Simon Josefsson.
++
++ 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 3 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, see <
http://www.gnu.org/licenses/>. */
++
++#include <config.h>
++
++#include "base64.h"
++
++#include <stddef.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdint.h>
++
++#include "macros.h"
++
++int
++main (void)
++{
++ const char *in = "abcdefghijklmnop";
++ const char *b64in = "YWJjZGVmZw==";
++ char out[255];
++ size_t len;
++ bool ok;
++ char *p;
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 0, out, 0);
++ ASSERT (out[0] == '\x42');
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 1, out, 1);
++ ASSERT (memcmp (out, "YQ==", 1) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 1, out, 2);
++ ASSERT (memcmp (out, "YQ==", 2) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 1, out, 3);
++ ASSERT (memcmp (out, "YQ==", 3) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 1, out, 4);
++ ASSERT (memcmp (out, "YQ==", 4) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 1, out, 8);
++ ASSERT (memcmp (out, "YQ==", 4) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 2, out, 4);
++ ASSERT (memcmp (out, "YWI=", 4) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 3, out, 4);
++ ASSERT (memcmp (out, "YWJj", 4) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 4, out, 5);
++ ASSERT (memcmp (out, "YWJjZA==", 5) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ base64_encode (in, 4, out, 100);
++ ASSERT (memcmp (out, "YWJjZA==", 6) == 0);
++
++ /* Decode. */
++
++ memset (out, 0x42, sizeof (out));
++ len = 0;
++ ok = base64_decode (b64in, 4, out, &len);
++ ASSERT (ok);
++ ASSERT (len == 0);
++
++ memset (out, 0x42, sizeof (out));
++ len = 1;
++ ok = base64_decode (b64in, 4, out, &len);
++ ASSERT (ok);
++ ASSERT (len == 1);
++ ASSERT (memcmp (out, "abcdefg", 1) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ len = 2;
++ ok = base64_decode (b64in, 4, out, &len);
++ ASSERT (ok);
++ ASSERT (len == 2);
++ ASSERT (memcmp (out, "abcdefg", 2) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ len = 3;
++ ok = base64_decode (b64in, 4, out, &len);
++ ASSERT (ok);
++ ASSERT (len == 3);
++ ASSERT (memcmp (out, "abcdefg", 3) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ len = 4;
++ ok = base64_decode (b64in, 4, out, &len);
++ ASSERT (ok);
++ ASSERT (len == 3);
++ ASSERT (memcmp (out, "abcdefg", 3) == 0);
++
++ memset (out, 0x42, sizeof (out));
++ len = 100;
++ ok = base64_decode (b64in, strlen (b64in), out, &len);
++ ASSERT (ok);
++ ASSERT (len == 7);
++ ASSERT (memcmp (out, "abcdefg", 7) == 0);
++
++ /* Allocating encode */
++
++ len = base64_encode_alloc (in, strlen (in), &p);
++ ASSERT (len == 24);
++ ASSERT (strcmp (p, "YWJjZGVmZ2hpamtsbW5vcA==") == 0);
++ free (p);
++
++ len = base64_encode_alloc (in, SIZE_MAX - 5, &p);
++ ASSERT (len == 0);
++
++ /* Decode context function */
++ {
++ struct base64_decode_context ctx;
++
++ base64_decode_ctx_init (&ctx);
++
++ len = sizeof (out);
++ ok = base64_decode_ctx (&ctx, b64in, strlen (b64in), out, &len);
++ ASSERT (ok);
++ ASSERT (len == 7);
++ ASSERT (memcmp (out, "abcdefg", len) == 0);
++ }
++
++ /* Allocating decode context function */
++
++ ok = base64_decode_alloc_ctx (NULL, b64in, strlen (b64in), &p, &len);
++ ASSERT (ok);
++ ASSERT (len == 7);
++ ASSERT (memcmp (out, "abcdefg", len) == 0);
++ free (p);
++
++ {
++ struct base64_decode_context ctx;
++ const char *newlineb64 = "YWJjZG\nVmZ2hp\namtsbW5vcA==";
++
++ base64_decode_ctx_init (&ctx);
++
++ ok = base64_decode_alloc_ctx (&ctx, newlineb64, strlen (newlineb64), &p,
&len);
++ ASSERT (ok);
++ ASSERT (len == strlen (in));
++ ASSERT (memcmp (p, in, len) == 0);
++ free (p);
++ }
++
++ {
++ struct base64_decode_context ctx;
++ base64_decode_ctx_init (&ctx);
++
++ ok = base64_decode_alloc_ctx (&ctx, "YW\nJjZGVmZ2hp", 13, &p,
&len);
++ ASSERT (ok);
++ ASSERT (len == 9);
++ ASSERT (memcmp (p, "abcdefghi", len) == 0);
++ free (p);
++
++ base64_decode_ctx_init (&ctx);
++
++ ok = base64_decode_alloc_ctx (&ctx, "YW\n", 3, &p, &len);
++ ASSERT (ok);
++ ASSERT (len == 0);
++ free (p);
++
++ ok = base64_decode_alloc_ctx (&ctx, "JjZGVmZ2", 8, &p, &len);
++ ASSERT (ok);
++ ASSERT (len == 6);
++ ASSERT (memcmp (p, "abcdef", len) == 0);
++ free (p);
++
++ ok = base64_decode_alloc_ctx (&ctx, "hp", 2, &p, &len);
++ ASSERT (ok);
++ ASSERT (len == 3);
++ ASSERT (memcmp (p, "ghi", len) == 0);
++ free (p);
++
++ ok = base64_decode_alloc_ctx (&ctx, "", 0, &p, &len);
++ ASSERT (ok);
++ free (p);
++ }
++
++ {
++ struct base64_decode_context ctx;
++ const char *newlineb64 = "\n\n\n\n\n";
++
++ base64_decode_ctx_init (&ctx);
++
++ ok = base64_decode_alloc_ctx (&ctx, newlineb64, strlen (newlineb64), &p,
&len);
++ ASSERT (ok);
++ ASSERT (len == 0);
++ free (p);
++ }
++
++ ok = base64_decode_alloc_ctx (NULL, " ! ", 3, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "abc\ndef", 7, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aa", 2, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aa=", 3, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aax", 3, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aa=X", 4, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aa=X", 4, &p, &len);
++ ASSERT (!ok);
++
++ ok = base64_decode_alloc_ctx (NULL, "aax=X", 5, &p, &len);
++ ASSERT (!ok);
++
++ return 0;
++}
+diff -urN libguestfs-1.28.2.old/m4/base64.m4 libguestfs-1.28.2/m4/base64.m4
+--- libguestfs-1.28.2.old/m4/base64.m4 1970-01-01 01:00:00.000000000 +0100
++++ libguestfs-1.28.2/m4/base64.m4 2014-10-31 21:57:50.000000000 +0000
+@@ -0,0 +1,15 @@
++# base64.m4 serial 4
++dnl Copyright (C) 2004, 2006, 2009-2014 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++AC_DEFUN([gl_FUNC_BASE64],
++[
++ gl_PREREQ_BASE64
++])
++
++# Prerequisites of lib/base64.c.
++AC_DEFUN([gl_PREREQ_BASE64], [
++ AC_REQUIRE([AC_C_RESTRICT])
++])
+diff -urN libguestfs-1.28.2.old/m4/gnulib-comp.m4 libguestfs-1.28.2/m4/gnulib-comp.m4
+--- libguestfs-1.28.2.old/m4/gnulib-comp.m4 2014-10-27 14:46:40.000000000 +0000
++++ libguestfs-1.28.2/m4/gnulib-comp.m4 2014-10-31 21:59:41.000000000 +0000
+@@ -57,6 +57,8 @@
+ # Code from module arpa_inet:
+ # Code from module arpa_inet-tests:
+ # Code from module at-internal:
++ # Code from module base64:
++ # Code from module base64-tests:
+ # Code from module binary-io:
+ # Code from module binary-io-tests:
+ # Code from module bind:
+@@ -502,6 +504,7 @@
+ gl_HEADER_ARPA_INET
+ AC_PROG_MKDIR_P
+ AC_LIBOBJ([openat-proc])
++ gl_FUNC_BASE64
+ gl_BYTESWAP
+ AC_CHECK_FUNCS_ONCE([readlinkat])
+ gl_UNISTD_MODULE_INDICATOR([chdir])
+@@ -1477,6 +1480,8 @@
+ lib/asnprintf.c
+ lib/asprintf.c
+ lib/at-func.c
++ lib/base64.c
++ lib/base64.h
+ lib/basename-lgpl.c
+ lib/binary-io.c
+ lib/binary-io.h
+@@ -1758,6 +1763,7 @@
+ m4/accept4.m4
+ m4/alloca.m4
+ m4/arpa_inet_h.m4
++ m4/base64.m4
+ m4/btowc.m4
+ m4/byteswap.m4
+ m4/chdir-long.m4
+@@ -2008,6 +2014,7 @@
+ tests/test-areadlinkat.c
+ tests/test-argmatch.c
+ tests/test-arpa_inet.c
++ tests/test-base64.c
+ tests/test-binary-io.c
+ tests/test-binary-io.sh
+ tests/test-bind.c
diff --git a/libguestfs.spec b/libguestfs.spec
index e2b1093..0d57d3a 100644
--- a/libguestfs.spec
+++ b/libguestfs.spec
@@ -25,13 +25,21 @@ Summary: Access and modify virtual machine disk images
Name: libguestfs
Epoch: 1
Version: 1.28.2
-Release: 1%{?dist}
+Release: 2%{?dist}
License: LGPLv2+
# Source and patches.
URL:
http://libguestfs.org/
Source0:
http://libguestfs.org/download/1.28-stable/%{name}-%{version}.tar.gz
+# Fix for
https://bugzilla.redhat.com/show_bug.cgi?id=1159016
+# libvirt backend does not set RBD password
+Patch1: 0001-launch-libvirt-Implement-drive-secrets-RHBZ-1159016.patch
+# Unfortunately because patch1 patches gnulib configuration, we
+# also need:
+Patch2: add-base64-to-gnulib.patch
+BuildRequires: autoconf, automake, libtool
+
# Basic build requirements:
BuildRequires: perl(Pod::Simple)
BuildRequires: perl(Pod::Man)
@@ -728,6 +736,10 @@ for %{name}.
%prep
%setup -q
+%patch1 -p1
+%patch2 -p1
+autoreconf -i
+
if [ "$(getenforce | tr '[A-Z]' '[a-z]')" !=
"disabled" ]; then
# For sVirt to work, the local temporary directory we use in the
# tests must be labelled the same way as /tmp.
@@ -1226,6 +1238,9 @@ popd
%changelog
+* Fri Oct 31 2014 Richard W.M. Jones <rjones(a)redhat.com> - 1:1.28.2-2
+- Fix: libvirt backend does not set RBD password (RHBZ#1159016).
+
* Mon Oct 27 2014 Richard W.M. Jones <rjones(a)redhat.com> - 1:1.28.2-1
- New upstream version 1.28.2.