1
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
2
3
+ use std:: env;
3
4
use std:: ffi:: { c_char, CString , OsStr } ;
4
5
use std:: fs:: { self , OpenOptions , Permissions } ;
5
- use std:: io:: { self , ErrorKind } ;
6
+ use std:: io:: { self , ErrorKind , Write } ;
6
7
use std:: os:: unix:: ffi:: OsStrExt ;
7
8
use std:: os:: unix:: fs:: { MetadataExt , PermissionsExt } ;
8
9
use std:: process:: { Command , Stdio } ;
@@ -336,14 +337,17 @@ pub struct VmImageInfo {
336
337
#[ serde( skip) ]
337
338
pub path : Utf8PathBuf ,
338
339
340
+ #[ serde( skip) ]
341
+ pub arch : Option < String > ,
342
+
339
343
#[ serde( rename = "virtual-size" ) ]
340
344
pub size : u64 ,
341
345
342
346
pub format : String ,
343
347
}
344
348
345
349
impl VmImageInfo {
346
- pub fn of ( vm_image_path : impl AsRef < Utf8Path > ) -> Result < VmImageInfo > {
350
+ pub fn of ( vm_image_path : impl AsRef < Utf8Path > , identify_arch : bool ) -> Result < VmImageInfo > {
347
351
let vm_image_path = vm_image_path. as_ref ( ) . to_path_buf ( ) ;
348
352
349
353
let output = Command :: new ( "qemu-img" )
@@ -362,6 +366,12 @@ impl VmImageInfo {
362
366
let mut info: VmImageInfo = serde_json:: from_slice ( & output. stdout ) ?;
363
367
info. path = vm_image_path;
364
368
369
+ if identify_arch {
370
+ info. arch = identify_image_arch ( & info. path ) ?
371
+ . ok_or_else ( || anyhow ! ( "Could not identify VM image architecture" ) ) ?
372
+ . into ( ) ;
373
+ }
374
+
365
375
Ok ( info)
366
376
}
367
377
}
@@ -393,6 +403,62 @@ pub fn create_overlay_vm_image(
393
403
Ok ( ( ) )
394
404
}
395
405
406
+ pub fn identify_image_arch ( image_path : impl AsRef < Utf8Path > ) -> Result < Option < String > > {
407
+ let xml = virt_inspector ( [
408
+ "--add" ,
409
+ image_path. as_ref ( ) . as_str ( ) ,
410
+ "--no-applications" ,
411
+ "--no-icon" ,
412
+ ] ) ?;
413
+
414
+ xpath ( & xml, "string(//arch)" )
415
+ }
416
+
417
+ fn virt_inspector ( args : impl IntoIterator < Item = impl AsRef < OsStr > > ) -> Result < String > {
418
+ let cache_dir = format ! ( "/var/tmp/crun-vm-{}" , env:: var( "_CONTAINERS_ROOTLESS_UID" ) ?) ;
419
+ fs:: create_dir_all ( & cache_dir) ?;
420
+
421
+ let output = Command :: new ( "virt-inspector" )
422
+ . args ( args)
423
+ . env ( "LIBGUESTFS_BACKEND" , "direct" )
424
+ . env ( "LIBGUESTFS_CACHEDIR" , & cache_dir)
425
+ . output ( ) ?;
426
+
427
+ ensure ! (
428
+ output. status. success( ) ,
429
+ "virt-inspector failed: {}" ,
430
+ String :: from_utf8_lossy( & output. stderr) ,
431
+ ) ;
432
+
433
+ Ok ( String :: from_utf8 ( output. stdout ) ?)
434
+ }
435
+
436
+ fn xpath ( xml : & str , path : & str ) -> Result < Option < String > > {
437
+ let mut child = Command :: new ( "virt-inspector" )
438
+ . arg ( "--xpath" )
439
+ . arg ( path)
440
+ . stdin ( Stdio :: piped ( ) )
441
+ . spawn ( ) ?;
442
+
443
+ child. stdin . take ( ) . unwrap ( ) . write_all ( xml. as_bytes ( ) ) ?;
444
+
445
+ let output = child. wait_with_output ( ) ?;
446
+
447
+ ensure ! (
448
+ output. status. success( ) ,
449
+ "virt-inspector --xpath failed: {}" ,
450
+ String :: from_utf8_lossy( & output. stderr) ,
451
+ ) ;
452
+
453
+ let result = String :: from_utf8 ( output. stdout ) ?;
454
+
455
+ if result. is_empty ( ) {
456
+ Ok ( None )
457
+ } else {
458
+ Ok ( Some ( result) )
459
+ }
460
+ }
461
+
396
462
/// Run `crun`.
397
463
///
398
464
/// `crun` will inherit this process' standard streams.
0 commit comments