Skip to content

Commit d576486

Browse files
committed
base_disks: Close race with concurrent creation
If two concurrent runs race to create a base disk, they could conflict. Use a tempfile to avoid that. Signed-off-by: Colin Walters <[email protected]>
1 parent c4cbfcd commit d576486

File tree

1 file changed

+41
-25
lines changed

1 file changed

+41
-25
lines changed

crates/kit/src/libvirt/base_disks.rs

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ pub fn find_or_create_base_disk(
6060
}
6161

6262
// Base disk doesn't exist or was stale, create it
63+
// Multiple concurrent processes may race to create this, but each uses
64+
// a unique temp file, so they won't conflict
6365
info!("Creating base disk: {:?}", base_disk_path);
6466
create_base_disk(
6567
&base_disk_path,
@@ -85,16 +87,23 @@ fn create_base_disk(
8587
use crate::run_ephemeral::CommonVmOpts;
8688
use crate::to_disk::{Format, ToDiskAdditionalOpts, ToDiskOpts};
8789

88-
// Use a temporary location during installation to avoid caching incomplete disks
89-
let temp_disk_path = base_disk_path.with_extension("qcow2.tmp");
90+
// Use a unique temporary file to avoid conflicts when multiple processes
91+
// race to create the same base disk
92+
let temp_file = tempfile::Builder::new()
93+
.prefix(&format!("{}.", base_disk_path.file_stem().unwrap()))
94+
.suffix(".tmp.qcow2")
95+
.tempfile_in(base_disk_path.parent().unwrap())
96+
.with_context(|| {
97+
format!(
98+
"Failed to create temp file in {:?}",
99+
base_disk_path.parent()
100+
)
101+
})?;
90102

91-
// Helper to cleanup temp disk on error
92-
let cleanup_temp_disk = || {
93-
if temp_disk_path.exists() {
94-
debug!("Cleaning up temporary base disk: {:?}", temp_disk_path);
95-
let _ = fs::remove_file(&temp_disk_path);
96-
}
97-
};
103+
let temp_disk_path = Utf8PathBuf::from(temp_file.path().to_str().unwrap());
104+
105+
// Keep the temp file open so it gets cleaned up automatically if we error out
106+
// We'll persist it manually on success
98107

99108
// Create the disk using to_disk at temporary location
100109
let to_disk_opts = ToDiskOpts {
@@ -120,12 +129,9 @@ fn create_base_disk(
120129
};
121130

122131
// Run bootc install - if it succeeds, the disk is valid
123-
if let Err(e) = crate::to_disk::run(to_disk_opts) {
124-
cleanup_temp_disk();
125-
return Err(e).with_context(|| {
126-
format!("Failed to install bootc to base disk: {:?}", temp_disk_path)
127-
});
128-
}
132+
// On error, temp_file is automatically cleaned up when dropped
133+
crate::to_disk::run(to_disk_opts)
134+
.with_context(|| format!("Failed to install bootc to base disk: {:?}", temp_disk_path))?;
129135

130136
// If we got here, bootc install succeeded - verify metadata was written
131137
let metadata_valid = crate::cache_metadata::check_cached_disk(
@@ -138,15 +144,25 @@ fn create_base_disk(
138144

139145
match metadata_valid {
140146
Ok(()) => {
141-
// All validations passed - move to final location
142-
if let Err(e) = fs::rename(&temp_disk_path, base_disk_path) {
143-
cleanup_temp_disk();
144-
return Err(e).with_context(|| {
145-
format!(
146-
"Failed to move validated base disk from {:?} to {:?}",
147-
temp_disk_path, base_disk_path
148-
)
149-
});
147+
// All validations passed - persist temp file to final location
148+
// If another concurrent process already created the file, that's fine
149+
match temp_file.persist(base_disk_path) {
150+
Ok(_) => {
151+
debug!("Successfully created base disk: {:?}", base_disk_path);
152+
}
153+
Err(e) if e.error.kind() == std::io::ErrorKind::AlreadyExists => {
154+
// Another process won the race and created the base disk
155+
debug!(
156+
"Base disk already created by another process: {:?}",
157+
base_disk_path
158+
);
159+
// temp file is cleaned up when e is dropped
160+
}
161+
Err(e) => {
162+
return Err(e.error).with_context(|| {
163+
format!("Failed to persist base disk to {:?}", base_disk_path)
164+
});
165+
}
150166
}
151167

152168
// Refresh libvirt storage pool so the new disk is visible to virsh
@@ -171,7 +187,7 @@ fn create_base_disk(
171187
Ok(())
172188
}
173189
Err(e) => {
174-
cleanup_temp_disk();
190+
// temp_file will be automatically cleaned up when dropped
175191
Err(eyre!("Generated disk metadata validation failed: {e}"))
176192
}
177193
}

0 commit comments

Comments
 (0)