qemu: do crash safe creation of NVRAM file

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.

Reviewed-by: Ján Tomko <jtomko@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2022-02-03 16:15:51 +00:00
parent 2df9031a0e
commit 5e3498744e

View File

@ -4433,6 +4433,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
bool created = false; bool created = false;
const char *master_nvram_path; const char *master_nvram_path;
ssize_t r; ssize_t r;
g_autofree char *tmp_dst_path = NULL;
if (!loader || !loader->nvram || virFileExists(loader->nvram)) if (!loader || !loader->nvram || virFileExists(loader->nvram))
return 0; return 0;
@ -4463,14 +4464,15 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
goto cleanup; 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, O_WRONLY | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR, S_IRUSR | S_IWUSR,
cfg->user, cfg->group, cfg->user, cfg->group,
VIR_FILE_OPEN_FORCE_OWNER)) < 0) { VIR_FILE_OPEN_FORCE_OWNER)) < 0) {
virReportSystemError(-dstFD, virReportSystemError(-dstFD,
_("Failed to create file '%s'"), _("Failed to create file '%s'"),
loader->nvram); tmp_dst_path);
goto cleanup; goto cleanup;
} }
@ -4489,7 +4491,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
if (safewrite(dstFD, buf, r) < 0) { if (safewrite(dstFD, buf, r) < 0) {
virReportSystemError(errno, virReportSystemError(errno,
_("Unable to write to file '%s'"), _("Unable to write to file '%s'"),
loader->nvram); tmp_dst_path);
goto cleanup; goto cleanup;
} }
} while (r); } while (r);
@ -4500,9 +4502,23 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
master_nvram_path); master_nvram_path);
goto cleanup; goto cleanup;
} }
if (g_fsync(dstFD) < 0) {
virReportSystemError(errno, _("cannot sync file '%s'"),
tmp_dst_path);
goto cleanup;
}
if (VIR_CLOSE(dstFD) < 0) { if (VIR_CLOSE(dstFD) < 0) {
virReportSystemError(errno, virReportSystemError(errno,
_("Unable to close file '%s'"), _("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); loader->nvram);
goto cleanup; goto cleanup;
} }
@ -4513,7 +4529,7 @@ qemuPrepareNVRAM(virQEMUDriver *driver,
* copy the file content. Roll back. */ * copy the file content. Roll back. */
if (ret < 0) { if (ret < 0) {
if (created) if (created)
unlink(loader->nvram); unlink(tmp_dst_path);
} }
VIR_FORCE_CLOSE(srcFD); VIR_FORCE_CLOSE(srcFD);