From c5b0b3ef9d4eb1ae07ef2ee83d8bfd8f7b4d4eac Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Apr 15 2015 18:40:41 +0000 Subject: 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) --- diff --git a/0001-lxc-create-the-required-directories-upon-driver-star.patch b/0001-lxc-create-the-required-directories-upon-driver-star.patch new file mode 100644 index 0000000..1e9df00 --- /dev/null +++ b/0001-lxc-create-the-required-directories-upon-driver-star.patch @@ -0,0 +1,35 @@ +From: Lubomir Rintel +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 +(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, diff --git a/0002-virNetSocketNewConnectUNIX-Use-flocks-when-spawning-.patch b/0002-virNetSocketNewConnectUNIX-Use-flocks-when-spawning-.patch new file mode 100644 index 0000000..70cce7b --- /dev/null +++ b/0002-virNetSocketNewConnectUNIX-Use-flocks-when-spawning-.patch @@ -0,0 +1,267 @@ +From: Michal Privoznik +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 +Signed-off-by: Michal Privoznik +(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 diff --git a/0003-virsh-Improve-change-media-success-message.patch b/0003-virsh-Improve-change-media-success-message.patch new file mode 100644 index 0000000..8791b5e --- /dev/null +++ b/0003-virsh-Improve-change-media-success-message.patch @@ -0,0 +1,58 @@ +From: Cole Robinson +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; diff --git a/0004-tests-rename-testStripIPv6BracketsData-to-testStripD.patch b/0004-tests-rename-testStripIPv6BracketsData-to-testStripD.patch new file mode 100644 index 0000000..e4e2ee0 --- /dev/null +++ b/0004-tests-rename-testStripIPv6BracketsData-to-testStripD.patch @@ -0,0 +1,41 @@ +From: =?UTF-8?q?J=C3=A1n=20Tomko?= +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, \ + }; \ diff --git a/0005-Add-functions-dealing-with-control-characters-in-str.patch b/0005-Add-functions-dealing-with-control-characters-in-str.patch new file mode 100644 index 0000000..dca9932 --- /dev/null +++ b/0005-Add-functions-dealing-with-control-characters-in-str.patch @@ -0,0 +1,155 @@ +From: =?UTF-8?q?J=C3=A1n=20Tomko?= +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; + } + diff --git a/0006-Strip-control-characters-from-sysfs-attributes.patch b/0006-Strip-control-characters-from-sysfs-attributes.patch new file mode 100644 index 0000000..9c14e89 --- /dev/null +++ b/0006-Strip-control-characters-from-sysfs-attributes.patch @@ -0,0 +1,36 @@ +From: =?UTF-8?q?J=C3=A1n=20Tomko?= +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; diff --git a/0007-Ignore-storage-volumes-with-control-codes-in-their-n.patch b/0007-Ignore-storage-volumes-with-control-codes-in-their-n.patch new file mode 100644 index 0000000..8a067b4 --- /dev/null +++ b/0007-Ignore-storage-volumes-with-control-codes-in-their-n.patch @@ -0,0 +1,29 @@ +From: =?UTF-8?q?J=C3=A1n=20Tomko?= +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; + diff --git a/0008-Strip-control-codes-in-virBufferEscapeString.patch b/0008-Strip-control-codes-in-virBufferEscapeString.patch new file mode 100644 index 0000000..845f1c4 --- /dev/null +++ b/0008-Strip-control-codes-in-virBufferEscapeString.patch @@ -0,0 +1,133 @@ +From: =?UTF-8?q?J=C3=A1n=20Tomko?= +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, "\n"); ++ virBufferAdjustIndent(&buf, 2); ++ virBufferEscapeString(&buf, "%s\n", data->data); ++ virBufferAdjustIndent(&buf, -2); ++ virBufferAddLit(&buf, ""); ++ ++ 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("\n", "\n \n"); + DO_TEST_ADD_STR("\n \n\n", "\n \n \n \n"); + ++#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("", ++ "\n <td></td><td></td>\n"); ++ DO_TEST_ESCAPE("\007\"&&\"\x15", ++ "\n "&&"\n"); ++ DO_TEST_ESCAPE(",,'..',,", ++ "\n ,,'..',,\n"); ++ DO_TEST_ESCAPE("\x01\x01\x02\x03\x05\x08", ++ "\n \n"); ++ + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + } + diff --git a/libvirt.spec b/libvirt.spec index 8617b8d..e17ce05 100644 --- a/libvirt.spec +++ b/libvirt.spec @@ -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 - 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 - 1.2.14-1 - Rebased to version 1.2.14