If we crash part way through writing the NVRAM file we end up with an
unusable NVRAM on file. To avoid this we need to write to a temporary
file and fsync(2) at the end, then rename to the real NVRAM file path.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/qemu/qemu_process.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index c13280c8f3..bc7c2a4dbc 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -4421,6 +4421,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
bool created = false;
const char *master_nvram_path;
ssize_t r;
+ g_autofree char *tmp_dst_path = NULL;
if (!loader || !loader->nvram || virFileExists(loader->nvram))
return 0;
@@ -4451,14 +4452,15 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
goto cleanup;
}
- if ((dstFD = virFileOpenAs(loader->nvram,
+ tmp_dst_path = g_strdup_printf("%s.tmp", loader->nvram);
+ if ((dstFD = virFileOpenAs(tmp_dst_path,
O_WRONLY | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR,
cfg->user, cfg->group,
VIR_FILE_OPEN_FORCE_OWNER)) < 0) {
virReportSystemError(-dstFD,
_("Failed to create file '%s'"),
- loader->nvram);
+ tmp_dst_path);
goto cleanup;
}
@@ -4477,7 +4479,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
if (safewrite(dstFD, buf, r) < 0) {
virReportSystemError(errno,
_("Unable to write to file '%s'"),
- loader->nvram);
+ tmp_dst_path);
goto cleanup;
}
} while (r);
@@ -4488,9 +4490,23 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
master_nvram_path);
goto cleanup;
}
+
+ if (g_fsync(dstFD) < 0) {
+ virReportSystemError(errno, _("cannot sync file '%s'"),
+ tmp_dst_path);
+ goto cleanup;
+ }
+
if (VIR_CLOSE(dstFD) < 0) {
virReportSystemError(errno,
_("Unable to close file '%s'"),
+ tmp_dst_path);
+ goto cleanup;
+ }
+
+ if (rename(tmp_dst_path, loader->nvram) < 0) {
+ virReportSystemError(errno,
+ _("Unable to replace '%s'"),
loader->nvram);
goto cleanup;
}
@@ -4501,7 +4517,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
* copy the file content. Roll back. */
if (ret < 0) {
if (created)
- unlink(loader->nvram);
+ unlink(tmp_dst_path);
}
VIR_FORCE_CLOSE(srcFD);
--
2.34.1