Fix LXC domain startup (bz #1210397)

Fix race starting multiple session daemons (bz #1200149)
Fix change-media success messages
Strip invalid control codes from XML (bz #1066564, bz #1184131)
This commit is contained in:
Cole Robinson 2015-04-15 14:40:41 -04:00
parent c0a04cb876
commit c5b0b3ef9d
9 changed files with 774 additions and 1 deletions

View File

@ -0,0 +1,35 @@
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Wed, 8 Apr 2015 19:16:52 +0200
Subject: [PATCH] lxc: create the required directories upon driver start
/var/run may reside on a tmpfs and we fail to create the PID file if
/var/run/lxc does not exist.
Since commit 0a8addc1, the lxc driver's state directory isn't
automatically created before starting a domain. Now, the lxc driver
makes sure the state directory exists when it initializes.
Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
(cherry picked from commit da33a1ac1f6c0ae2ebe72bc385bbc7c407026956)
---
src/lxc/lxc_driver.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 245000d..8dfa686 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -1648,6 +1648,13 @@ static int lxcStateInitialize(bool privileged,
if (!(caps = virLXCDriverGetCapabilities(lxc_driver, false)))
goto cleanup;
+ if (virFileMakePath(cfg->stateDir) < 0) {
+ virReportSystemError(errno,
+ _("Failed to mkdir %s"),
+ cfg->stateDir);
+ goto cleanup;
+ }
+
/* Get all the running persistent or transient configs first */
if (virDomainObjListLoadAllConfigs(lxc_driver->domains,
cfg->stateDir,

View File

@ -0,0 +1,267 @@
From: Michal Privoznik <mprivozn@redhat.com>
Date: Thu, 2 Apr 2015 14:41:17 +0200
Subject: [PATCH] virNetSocketNewConnectUNIX: Use flocks when spawning a daemon
https://bugzilla.redhat.com/show_bug.cgi?id=1200149
Even though we have a mutex mechanism so that two clients don't spawn
two daemons, it's not strong enough. It can happen that while one
client is spawning the daemon, the other one fails to connect.
Basically two possible errors can happen:
error: Failed to connect socket to '/home/mprivozn/.cache/libvirt/libvirt-sock': Connection refused
or:
error: Failed to connect socket to '/home/mprivozn/.cache/libvirt/libvirt-sock': No such file or directory
The problem in both cases is, the daemon is only starting up, while we
are trying to connect (and fail). We should postpone the connecting
phase until the daemon is started (by the other thread that is
spawning it). In order to do that, create a file lock 'libvirt-lock'
in the directory where session daemon would create its socket. So even
when called from multiple processes, spawning a daemon will serialize
on the file lock. So only the first to come will spawn the daemon.
Tested-by: Richard W. M. Jones <rjones@redhat.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit be78814ae07f092d9c4e71fd82dd1947aba2f029)
---
src/rpc/virnetsocket.c | 164 +++++++++++++++++--------------------------------
1 file changed, 55 insertions(+), 109 deletions(-)
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
index 6b019cc..b824285 100644
--- a/src/rpc/virnetsocket.c
+++ b/src/rpc/virnetsocket.c
@@ -123,7 +123,7 @@ VIR_ONCE_GLOBAL_INIT(virNetSocket)
#ifndef WIN32
-static int virNetSocketForkDaemon(const char *binary, int passfd)
+static int virNetSocketForkDaemon(const char *binary)
{
int ret;
virCommandPtr cmd = virCommandNewArgList(binary,
@@ -136,10 +136,6 @@ static int virNetSocketForkDaemon(const char *binary, int passfd)
virCommandAddEnvPassBlockSUID(cmd, "XDG_RUNTIME_DIR", NULL);
virCommandClearCaps(cmd);
virCommandDaemonize(cmd);
- if (passfd) {
- virCommandPassFD(cmd, passfd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
- virCommandPassListenFDs(cmd);
- }
ret = virCommandRun(cmd, NULL);
virCommandFree(cmd);
return ret;
@@ -543,45 +539,26 @@ int virNetSocketNewConnectUNIX(const char *path,
const char *binary,
virNetSocketPtr *retsock)
{
- char *binname = NULL;
- char *pidpath = NULL;
- int fd, passfd = -1;
- int pidfd = -1;
+ char *lockpath = NULL;
+ int lockfd = -1;
+ int fd = -1;
+ int retries = 100;
virSocketAddr localAddr;
virSocketAddr remoteAddr;
+ char *rundir = NULL;
memset(&localAddr, 0, sizeof(localAddr));
memset(&remoteAddr, 0, sizeof(remoteAddr));
remoteAddr.len = sizeof(remoteAddr.data.un);
- if (spawnDaemon && !binary) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Auto-spawn of daemon requested, but no binary specified"));
- return -1;
- }
-
- if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
- virReportSystemError(errno, "%s", _("Failed to create socket"));
- goto error;
- }
-
- remoteAddr.data.un.sun_family = AF_UNIX;
- if (virStrcpyStatic(remoteAddr.data.un.sun_path, path) == NULL) {
- virReportSystemError(ENOMEM, _("Path %s too long for unix socket"), path);
- goto error;
- }
- if (remoteAddr.data.un.sun_path[0] == '@')
- remoteAddr.data.un.sun_path[0] = '\0';
-
- retry:
- if (connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0) {
- int status = 0;
- pid_t pid = 0;
+ if (spawnDaemon) {
+ const char *binname;
- if (!spawnDaemon) {
- virReportSystemError(errno, _("Failed to connect socket to '%s'"),
- path);
+ if (spawnDaemon && !binary) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Auto-spawn of daemon requested, "
+ "but no binary specified"));
goto error;
}
@@ -592,90 +569,63 @@ int virNetSocketNewConnectUNIX(const char *path,
goto error;
}
- if (virPidFileConstructPath(false, NULL, binname, &pidpath) < 0)
+ if (!(rundir = virGetUserRuntimeDirectory()))
goto error;
- pidfd = virPidFileAcquirePath(pidpath, false, getpid());
- if (pidfd < 0) {
- /*
- * This can happen in a very rare case of two clients spawning two
- * daemons at the same time, and the error in the logs that gets
- * reset here can be a clue to some future debugging.
- */
- virResetLastError();
- spawnDaemon = false;
- goto retry;
- }
-
- if ((passfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
- virReportSystemError(errno, "%s", _("Failed to create socket"));
+ if (virFileMakePathWithMode(rundir, 0700) < 0) {
+ virReportSystemError(errno,
+ _("Cannot create user runtime directory '%s'"),
+ rundir);
goto error;
}
- /*
- * We already even acquired the pidfile, so no one else should be using
- * @path right now. So we're OK to unlink it and paying attention to
- * the return value makes no real sense here. Only if it's not an
- * abstract socket, of course.
- */
- if (path[0] != '@')
- unlink(path);
-
- /*
- * We have to fork() here, because umask() is set per-process, chmod()
- * is racy and fchmod() has undefined behaviour on sockets according to
- * POSIX, so it doesn't work outside Linux.
- */
- if ((pid = virFork()) < 0)
+ if (virAsprintf(&lockpath, "%s/%s.lock", rundir, binname) < 0)
goto error;
- if (pid == 0) {
- umask(0077);
- if (bind(passfd, &remoteAddr.data.sa, remoteAddr.len) < 0)
- _exit(EXIT_FAILURE);
-
- _exit(EXIT_SUCCESS);
- }
-
- if (virProcessWait(pid, &status, false) < 0)
+ if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0600)) < 0 ||
+ virSetCloseExec(lockfd) < 0) {
+ virReportSystemError(errno, _("Unable to create lock '%s'"), lockpath);
goto error;
-
- if (status != EXIT_SUCCESS) {
- /*
- * OK, so the child failed to bind() the socket. This may mean that
- * another daemon was starting at the same time and succeeded with
- * its bind() (even though it should not happen because we using a
- * pidfile for the race). So we'll try connecting again, but this
- * time without spawning the daemon.
- */
- spawnDaemon = false;
- virPidFileDeletePath(pidpath);
- VIR_FORCE_CLOSE(pidfd);
- VIR_FORCE_CLOSE(passfd);
- goto retry;
}
- if (listen(passfd, 0) < 0) {
- virReportSystemError(errno, "%s",
- _("Failed to listen on socket that's about "
- "to be passed to the daemon"));
+ if (virFileLock(lockfd, false, 0, 1, true) < 0) {
+ virReportSystemError(errno, _("Unable to lock '%s'"), lockpath);
goto error;
}
+ }
+
+ if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ virReportSystemError(errno, "%s", _("Failed to create socket"));
+ goto error;
+ }
- if (connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0) {
+ remoteAddr.data.un.sun_family = AF_UNIX;
+ if (virStrcpyStatic(remoteAddr.data.un.sun_path, path) == NULL) {
+ virReportSystemError(ENOMEM, _("Path %s too long for unix socket"), path);
+ goto error;
+ }
+ if (remoteAddr.data.un.sun_path[0] == '@')
+ remoteAddr.data.un.sun_path[0] = '\0';
+
+ while (retries &&
+ connect(fd, &remoteAddr.data.sa, remoteAddr.len) < 0) {
+ if (!(spawnDaemon && errno == ENOENT)) {
virReportSystemError(errno, _("Failed to connect socket to '%s'"),
path);
goto error;
}
- /*
- * Do we need to eliminate the super-rare race here any more? It would
- * need incorporating the following VIR_FORCE_CLOSE() into a
- * virCommandHook inside a virNetSocketForkDaemon().
- */
- VIR_FORCE_CLOSE(pidfd);
- if (virNetSocketForkDaemon(binary, passfd) < 0)
+ if (virNetSocketForkDaemon(binary) < 0)
goto error;
+
+ retries--;
+ usleep(5000);
+ }
+
+ if (lockfd) {
+ unlink(lockpath);
+ VIR_FORCE_CLOSE(lockfd);
+ VIR_FREE(lockpath);
}
localAddr.len = sizeof(localAddr.data);
@@ -687,19 +637,15 @@ int virNetSocketNewConnectUNIX(const char *path,
if (!(*retsock = virNetSocketNew(&localAddr, &remoteAddr, true, fd, -1, 0)))
goto error;
- VIR_FREE(pidpath);
-
return 0;
error:
- if (pidfd >= 0)
- virPidFileDeletePath(pidpath);
- VIR_FREE(pidpath);
+ if (lockfd)
+ unlink(lockpath);
+ VIR_FREE(lockpath);
+ VIR_FREE(rundir);
VIR_FORCE_CLOSE(fd);
- VIR_FORCE_CLOSE(passfd);
- VIR_FORCE_CLOSE(pidfd);
- if (spawnDaemon)
- unlink(path);
+ VIR_FORCE_CLOSE(lockfd);
return -1;
}
#else

View File

@ -0,0 +1,58 @@
From: Cole Robinson <crobinso@redhat.com>
Date: Mon, 6 Apr 2015 13:59:46 -0400
Subject: [PATCH] virsh: Improve change-media success message
$ sudo virsh change-media f19 hdc /mnt/data/devel/media/Fedora-16-x86_64-Live-KDE.iso
succeeded to complete action update on media
Change the message to:
Successfully {inserted,ejected,changed} media.
https://bugzilla.redhat.com/show_bug.cgi?id=967946
(cherry picked from commit e3aa4c91c8b54cdfb1c312a142fd9fb79daec65a)
---
tools/virsh-domain.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 9bbb964..a735649 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -12295,6 +12295,7 @@ cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
bool ret = false;
vshUpdateDiskXMLType update_type;
const char *action = NULL;
+ const char *success_msg = NULL;
bool config = vshCommandOptBool(cmd, "config");
bool live = vshCommandOptBool(cmd, "live");
bool current = vshCommandOptBool(cmd, "current");
@@ -12314,16 +12315,19 @@ cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
if (eject) {
update_type = VSH_UPDATE_DISK_XML_EJECT;
action = "eject";
+ success_msg = _("Successfully ejected media.");
}
if (insert) {
update_type = VSH_UPDATE_DISK_XML_INSERT;
action = "insert";
+ success_msg = _("Successfully inserted media.");
}
if (update || (!eject && !insert)) {
update_type = VSH_UPDATE_DISK_XML_UPDATE;
action = "update";
+ success_msg = _("Successfully updated media.");
}
VSH_EXCLUSIVE_OPTIONS_VAR(current, live);
@@ -12367,7 +12371,7 @@ cmdChangeMedia(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
- vshPrint(ctl, _("succeeded to complete action %s on media\n"), action);
+ vshPrint(ctl, "%s", success_msg);
}
ret = true;

View File

@ -0,0 +1,41 @@
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
Date: Tue, 14 Apr 2015 12:06:44 +0200
Subject: [PATCH] tests: rename testStripIPv6BracketsData to testStripData
For reuse with other Strip* functions.
(cherry picked from commit e892842dfd3c7bad8fbfbfcf0501d01804e9e7c3)
---
tests/virstringtest.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/virstringtest.c b/tests/virstringtest.c
index a0bfd61..9d0b438 100644
--- a/tests/virstringtest.c
+++ b/tests/virstringtest.c
@@ -522,14 +522,14 @@ testVirStringFreeListCount(const void *opaque ATTRIBUTE_UNUSED)
}
-struct testStripIPv6BracketsData {
+struct testStripData {
const char *string;
const char *result;
};
static int testStripIPv6Brackets(const void *args)
{
- const struct testStripIPv6BracketsData *data = args;
+ const struct testStripData *data = args;
int ret = -1;
char *res = NULL;
@@ -766,7 +766,7 @@ mymain(void)
#define TEST_STRIP_IPV6_BRACKETS(str, res) \
do { \
- struct testStripIPv6BracketsData stripData = { \
+ struct testStripData stripData = { \
.string = str, \
.result = res, \
}; \

View File

@ -0,0 +1,155 @@
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
Date: Tue, 14 Apr 2015 12:30:16 +0200
Subject: [PATCH] Add functions dealing with control characters in strings
Add virStringHasControlChars that checks if the string has
any control characters other than \t\r\n,
and virStringStripControlChars that removes them in-place.
(cherry picked from commit 2a530a3e50d9314950cff0a5790c81910b0750a9)
---
src/libvirt_private.syms | 2 ++
src/util/virstring.c | 42 ++++++++++++++++++++++++++++++++++++++++++
src/util/virstring.h | 2 ++
tests/virstringtest.c | 39 +++++++++++++++++++++++++++++++++++++++
4 files changed, 85 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 5716ece..21b6aa5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2121,6 +2121,7 @@ virStrdup;
virStringArrayHasString;
virStringFreeList;
virStringFreeListCount;
+virStringHasControlChars;
virStringIsEmpty;
virStringJoin;
virStringListLength;
@@ -2130,6 +2131,7 @@ virStringSortCompare;
virStringSortRevCompare;
virStringSplit;
virStringSplitCount;
+virStringStripControlChars;
virStringStripIPv6Brackets;
virStrncpy;
virStrndup;
diff --git a/src/util/virstring.c b/src/util/virstring.c
index 3dad9dd..1cd4987 100644
--- a/src/util/virstring.c
+++ b/src/util/virstring.c
@@ -968,3 +968,45 @@ virStringStripIPv6Brackets(char *str)
str[len - 2] = '\0';
}
}
+
+
+static const char control_chars[] =
+ "\x01\x02\x03\x04\x05\x06\x07"
+ "\x08" /* \t \n */ "\x0B\x0C" /* \r */ "\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+
+bool
+virStringHasControlChars(const char *str)
+{
+ if (!str)
+ return false;
+
+ return str[strcspn(str, control_chars)] != '\0';
+}
+
+
+/**
+ * virStringStripControlChars:
+ * @str: the string to strip
+ *
+ * Modify the string in-place to remove the control characters
+ * in the interval: [0x01, 0x20)
+ */
+void
+virStringStripControlChars(char *str)
+{
+ size_t len, i, j;
+
+ if (!str)
+ return;
+
+ len = strlen(str);
+ for (i = 0, j = 0; i < len; i++) {
+ if (index(control_chars, str[i]))
+ continue;
+
+ str[j++] = str[i];
+ }
+ str[j] = '\0';
+}
diff --git a/src/util/virstring.h b/src/util/virstring.h
index 2ec60fa..e6dcb32 100644
--- a/src/util/virstring.h
+++ b/src/util/virstring.h
@@ -271,5 +271,7 @@ char *virStringReplace(const char *haystack,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
void virStringStripIPv6Brackets(char *str);
+bool virStringHasControlChars(const char *str);
+void virStringStripControlChars(char *str);
#endif /* __VIR_STRING_H__ */
diff --git a/tests/virstringtest.c b/tests/virstringtest.c
index 9d0b438..38d0126 100644
--- a/tests/virstringtest.c
+++ b/tests/virstringtest.c
@@ -551,6 +551,29 @@ static int testStripIPv6Brackets(const void *args)
return ret;
}
+static int testStripControlChars(const void *args)
+{
+ const struct testStripData *data = args;
+ int ret = -1;
+ char *res = NULL;
+
+ if (VIR_STRDUP(res, data->string) < 0)
+ goto cleanup;
+
+ virStringStripControlChars(res);
+
+ if (STRNEQ_NULLABLE(res, data->result)) {
+ fprintf(stderr, "Returned '%s', expected '%s'\n",
+ NULLSTR(res), NULLSTR(data->result));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(res);
+ return ret;
+}
static int
mymain(void)
@@ -783,6 +806,22 @@ mymain(void)
TEST_STRIP_IPV6_BRACKETS(":hello]", ":hello]");
TEST_STRIP_IPV6_BRACKETS(":[]:", ":[]:");
+#define TEST_STRIP_CONTROL_CHARS(str, res) \
+ do { \
+ struct testStripData stripData = { \
+ .string = str, \
+ .result = res, \
+ }; \
+ if (virtTestRun("Strip control chars from " #str, \
+ testStripControlChars, &stripData) < 0) \
+ ret = -1; \
+ } while (0)
+
+ TEST_STRIP_CONTROL_CHARS(NULL, NULL);
+ TEST_STRIP_CONTROL_CHARS("\nhello \r hello\t", "\nhello \r hello\t");
+ TEST_STRIP_CONTROL_CHARS("\x01H\x02" "E\x03L\x04L\x05O", "HELLO");
+ TEST_STRIP_CONTROL_CHARS("\x01\x02\x03\x04HELL\x05O", "HELLO");
+ TEST_STRIP_CONTROL_CHARS("\nhello \x01\x07hello\t", "\nhello hello\t");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,36 @@
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
Date: Tue, 14 Apr 2015 12:30:34 +0200
Subject: [PATCH] Strip control characters from sysfs attributes
Including them in the XML makes them unparsable.
https://bugzilla.redhat.com/show_bug.cgi?id=1184131
(cherry picked from commit 557107500b22d4a5ba7d1b09f5f516512dfca67b)
---
src/node_device/node_device_udev.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 8c39e5f..0d691e0 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -194,7 +194,9 @@ static int udevGetUintProperty(struct udev_device *udev_device,
/* This function allocates memory from the heap for the property
- * value. That memory must be later freed by some other code. */
+ * value. That memory must be later freed by some other code.
+ * Any control characters that cannot be printed in the XML are stripped
+ * from the string */
static int udevGetDeviceSysfsAttr(struct udev_device *udev_device,
const char *attr_name,
char **attr_value)
@@ -239,6 +241,8 @@ static int udevGetStringSysfsAttr(struct udev_device *udev_device,
ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &tmp);
+ virStringStripControlChars(tmp);
+
if (tmp != NULL && (STREQ(tmp, ""))) {
VIR_FREE(tmp);
tmp = NULL;

View File

@ -0,0 +1,29 @@
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
Date: Tue, 14 Apr 2015 12:30:55 +0200
Subject: [PATCH] Ignore storage volumes with control codes in their names
To prevent generating invalid XML.
https://bugzilla.redhat.com/show_bug.cgi?id=1066564
(cherry picked from commit 60db2bc80fb5048b227c77c5138fe0e2c97e9c14)
---
src/storage/storage_backend_fs.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index 35385db..4561ee0 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -861,6 +861,12 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
while ((direrr = virDirRead(dir, &ent, pool->def->target.path)) > 0) {
int ret;
+ if (virStringHasControlChars(ent->d_name)) {
+ VIR_WARN("Ignoring file with control characters under '%s'",
+ pool->def->target.path);
+ continue;
+ }
+
if (VIR_ALLOC(vol) < 0)
goto error;

View File

@ -0,0 +1,133 @@
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
Date: Mon, 30 Mar 2015 12:41:40 +0200
Subject: [PATCH] Strip control codes in virBufferEscapeString
These cannot be represented in XML.
We have been stripping them, but only if the string had
characters that needed escaping: <>"'&
Extend the strcspn check to include control codes, and strip
them even if we don't do any escaping.
https://bugzilla.redhat.com/show_bug.cgi?id=1184131
https://bugzilla.redhat.com/show_bug.cgi?id=1066564
(cherry picked from commit aeb5262e4397528d582682471cb8075141189465)
---
src/util/virbuffer.c | 14 +++++++++++---
tests/virbuftest.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c
index 706dbfa..3d13c90 100644
--- a/src/util/virbuffer.c
+++ b/src/util/virbuffer.c
@@ -438,6 +438,13 @@ virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
int len;
char *escaped, *out;
const char *cur;
+ const char forbidden_characters[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ /*\t*/ /*\n*/ 0x0B, 0x0C, /*\r*/ 0x0E, 0x0F, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, '"', '&', '\'', '<', '>',
+ '\0'
+ };
if ((format == NULL) || (buf == NULL) || (str == NULL))
return;
@@ -446,7 +453,7 @@ virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
return;
len = strlen(str);
- if (strcspn(str, "<>&'\"") == len) {
+ if (strcspn(str, forbidden_characters) == len) {
virBufferAsprintf(buf, format, str);
return;
}
@@ -490,8 +497,7 @@ virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
*out++ = 'o';
*out++ = 's';
*out++ = ';';
- } else if (((unsigned char)*cur >= 0x20) || (*cur == '\n') || (*cur == '\t') ||
- (*cur == '\r')) {
+ } else if (!strchr(forbidden_characters, *cur)) {
/*
* default case, just copy !
* Note that character over 0x80 are likely to give problem
@@ -499,6 +505,8 @@ virBufferEscapeString(virBufferPtr buf, const char *format, const char *str)
* it's hard to handle properly we have to assume it's UTF-8 too
*/
*out++ = *cur;
+ } else {
+ /* silently ignore control characters */
}
cur++;
}
diff --git a/tests/virbuftest.c b/tests/virbuftest.c
index 21cb18b..10398d5 100644
--- a/tests/virbuftest.c
+++ b/tests/virbuftest.c
@@ -349,6 +349,39 @@ testBufAddStr(const void *opaque ATTRIBUTE_UNUSED)
static int
+testBufEscapeStr(const void *opaque ATTRIBUTE_UNUSED)
+{
+ const struct testBufAddStrData *data = opaque;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ char *actual;
+ int ret = -1;
+
+ virBufferAddLit(&buf, "<c>\n");
+ virBufferAdjustIndent(&buf, 2);
+ virBufferEscapeString(&buf, "<el>%s</el>\n", data->data);
+ virBufferAdjustIndent(&buf, -2);
+ virBufferAddLit(&buf, "</c>");
+
+ if (!(actual = virBufferContentAndReset(&buf))) {
+ TEST_ERROR("buf is empty");
+ goto cleanup;
+ }
+
+ if (STRNEQ_NULLABLE(actual, data->expect)) {
+ TEST_ERROR("testBufEscapeStr(): Strings don't match:\n");
+ virtTestDifference(stderr, data->expect, actual);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(actual);
+ return ret;
+}
+
+
+static int
mymain(void)
{
int ret = 0;
@@ -379,6 +412,22 @@ mymain(void)
DO_TEST_ADD_STR("<a/>\n", "<c>\n <a/>\n</c>");
DO_TEST_ADD_STR("<b>\n <a/>\n</b>\n", "<c>\n <b>\n <a/>\n </b>\n</c>");
+#define DO_TEST_ESCAPE(data, expect) \
+ do { \
+ struct testBufAddStrData info = { data, expect }; \
+ if (virtTestRun("Buf: EscapeStr", testBufEscapeStr, &info) < 0) \
+ ret = -1; \
+ } while (0)
+
+ DO_TEST_ESCAPE("<td></td><td></td>",
+ "<c>\n <el>&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;</el>\n</c>");
+ DO_TEST_ESCAPE("\007\"&&\"\x15",
+ "<c>\n <el>&quot;&amp;&amp;&quot;</el>\n</c>");
+ DO_TEST_ESCAPE(",,'..',,",
+ "<c>\n <el>,,&apos;..&apos;,,</el>\n</c>");
+ DO_TEST_ESCAPE("\x01\x01\x02\x03\x05\x08",
+ "<c>\n <el></el>\n</c>");
+
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -372,7 +372,7 @@
Summary: Library providing a simple virtualization API
Name: libvirt
Version: 1.2.14
Release: 1%{?dist}%{?extra_release}
Release: 2%{?dist}%{?extra_release}
License: LGPLv2+
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
@ -383,6 +383,19 @@ URL: http://libvirt.org/
%endif
Source: http://libvirt.org/sources/%{?mainturl}libvirt-%{version}.tar.gz
# Fix LXC domain startup (bz #1210397)
Patch0001: 0001-lxc-create-the-required-directories-upon-driver-star.patch
# Fix race starting multiple session daemons (bz #1200149)
Patch0002: 0002-virNetSocketNewConnectUNIX-Use-flocks-when-spawning-.patch
# Fix change-media success messages
Patch0003: 0003-virsh-Improve-change-media-success-message.patch
# Strip invalid control codes from XML (bz #1066564, bz #1184131)
Patch0004: 0004-tests-rename-testStripIPv6BracketsData-to-testStripD.patch
Patch0005: 0005-Add-functions-dealing-with-control-characters-in-str.patch
Patch0006: 0006-Strip-control-characters-from-sysfs-attributes.patch
Patch0007: 0007-Ignore-storage-volumes-with-control-codes-in-their-n.patch
Patch0008: 0008-Strip-control-codes-in-virBufferEscapeString.patch
%if %{with_libvirtd}
Requires: libvirt-daemon = %{version}-%{release}
%if %{with_network}
@ -2291,6 +2304,12 @@ exit 0
%doc examples/systemtap
%changelog
* Wed Apr 15 2015 Cole Robinson <crobinso@redhat.com> - 1.2.14-2
- Fix LXC domain startup (bz #1210397)
- Fix race starting multiple session daemons (bz #1200149)
- Fix change-media success messages
- Strip invalid control codes from XML (bz #1066564, bz #1184131)
* Thu Apr 02 2015 Cole Robinson <crobinso@redhat.com> - 1.2.14-1
- Rebased to version 1.2.14