@@ -60,6 +60,8 @@ pub fn find_or_create_base_disk(
60
60
}
61
61
62
62
// 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
63
65
info ! ( "Creating base disk: {:?}" , base_disk_path) ;
64
66
create_base_disk (
65
67
& base_disk_path,
@@ -85,16 +87,23 @@ fn create_base_disk(
85
87
use crate :: run_ephemeral:: CommonVmOpts ;
86
88
use crate :: to_disk:: { Format , ToDiskAdditionalOpts , ToDiskOpts } ;
87
89
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
+ } ) ?;
90
102
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
98
107
99
108
// Create the disk using to_disk at temporary location
100
109
let to_disk_opts = ToDiskOpts {
@@ -120,12 +129,9 @@ fn create_base_disk(
120
129
} ;
121
130
122
131
// 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) ) ?;
129
135
130
136
// If we got here, bootc install succeeded - verify metadata was written
131
137
let metadata_valid = crate :: cache_metadata:: check_cached_disk (
@@ -138,15 +144,25 @@ fn create_base_disk(
138
144
139
145
match metadata_valid {
140
146
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
+ }
150
166
}
151
167
152
168
// Refresh libvirt storage pool so the new disk is visible to virsh
@@ -171,7 +187,7 @@ fn create_base_disk(
171
187
Ok ( ( ) )
172
188
}
173
189
Err ( e) => {
174
- cleanup_temp_disk ( ) ;
190
+ // temp_file will be automatically cleaned up when dropped
175
191
Err ( eyre ! ( "Generated disk metadata validation failed: {e}" ) )
176
192
}
177
193
}
0 commit comments