@@ -8,9 +8,10 @@ use std::os::unix::io::AsRawFd;
8
8
9
9
use vm_memory:: VolatileSlice ;
10
10
11
- use libc:: { c_int, c_void, read, readv, size_t, write, writev} ;
12
-
13
11
use super :: bindings:: { off64_t, pread64, preadv64, pwrite64, pwritev64} ;
12
+ #[ cfg( feature = "blk" ) ]
13
+ use crate :: virtio:: block:: disk;
14
+ use libc:: { c_int, c_void, read, readv, size_t, write, writev} ;
14
15
15
16
/// A trait for setting the size of a file.
16
17
/// This is equivalent to File's `set_len` method, but
@@ -436,8 +437,27 @@ impl FileReadWriteAtVolatile for imago::SyncFormatAccess<imago::file::File> {
436
437
437
438
fn write_at_volatile ( & self , slice : VolatileSlice , offset : u64 ) -> Result < usize > {
438
439
let slices = & [ & slice] ;
439
- let iovec = imago:: io_buffers:: IoVector :: from_volatile_slice ( slices) ;
440
- self . writev ( iovec. 0 , offset) ?;
440
+
441
+ let ( mut iovec, _guard) = imago:: io_buffers:: IoVector :: from_volatile_slice ( slices) ;
442
+ guard_header_bytes ( & iovec, offset) ?;
443
+
444
+ let mut _copied_head = None ;
445
+ if offset == 0 {
446
+ let ( head, tail) = iovec. split_at ( std:: cmp:: max ( self . mem_align ( ) , 4 ) as u64 ) ;
447
+ _copied_head = Some ( head. try_into_owned ( self . mem_align ( ) ) ?) ;
448
+ let head_slice = _copied_head. as_ref ( ) . unwrap ( ) . as_ref ( ) . into_slice ( ) ;
449
+ #[ cfg( feature = "blk" ) ]
450
+ if head_slice. get ( 0 ..4 ) == Some ( & disk:: QCOW_MAGIC . to_be_bytes ( ) ) {
451
+ return Err ( Error :: new (
452
+ ErrorKind :: InvalidData ,
453
+ "writing QCOW2 header to file" ,
454
+ ) ) ;
455
+ }
456
+
457
+ iovec = tail. with_inserted ( 0 , head_slice) ;
458
+ }
459
+
460
+ self . writev ( iovec, offset) ?;
441
461
Ok ( slice. len ( ) )
442
462
}
443
463
@@ -446,7 +466,25 @@ impl FileReadWriteAtVolatile for imago::SyncFormatAccess<imago::file::File> {
446
466
return Ok ( 0 ) ;
447
467
}
448
468
449
- let ( iovec, _guard) = imago:: io_buffers:: IoVector :: from_volatile_slice ( bufs) ;
469
+ let ( mut iovec, _guard) = imago:: io_buffers:: IoVector :: from_volatile_slice ( bufs) ;
470
+ guard_header_bytes ( & iovec, offset) ?;
471
+
472
+ let mut _copied_head = None ;
473
+ if offset == 0 {
474
+ let ( head, tail) = iovec. split_at ( std:: cmp:: max ( self . mem_align ( ) , 4 ) as u64 ) ;
475
+ _copied_head = Some ( head. try_into_owned ( self . mem_align ( ) ) ?) ;
476
+ let head_slice = _copied_head. as_ref ( ) . unwrap ( ) . as_ref ( ) . into_slice ( ) ;
477
+ #[ cfg( feature = "blk" ) ]
478
+ if head_slice. get ( 0 ..4 ) == Some ( & disk:: QCOW_MAGIC . to_be_bytes ( ) ) {
479
+ return Err ( Error :: new (
480
+ ErrorKind :: InvalidData ,
481
+ "writing QCOW2 header to disk image" ,
482
+ ) ) ;
483
+ }
484
+
485
+ iovec = tail. with_inserted ( 0 , head_slice) ;
486
+ }
487
+
450
488
let full_length = iovec
451
489
. len ( )
452
490
. try_into ( )
@@ -455,3 +493,17 @@ impl FileReadWriteAtVolatile for imago::SyncFormatAccess<imago::file::File> {
455
493
Ok ( full_length)
456
494
}
457
495
}
496
+
497
+ fn guard_header_bytes ( iovec : & imago:: io_buffers:: IoVector , offset : u64 ) -> Result < ( ) > {
498
+ if ( 1 ..4 ) . contains ( & offset) || ( offset + iovec. len ( ) < 4 ) {
499
+ // Make sure the guest writes to the first four bytes as a whole. This is done to prevent a
500
+ // Raw disk image byte-by-byte, and in any order, writing the header of a formatted disk
501
+ // image into the first sector of the disk image.
502
+ return Err ( Error :: new (
503
+ ErrorKind :: InvalidInput ,
504
+ "partial write to first four bytes of disk image" ,
505
+ ) ) ;
506
+ }
507
+
508
+ Ok ( ( ) )
509
+ }
0 commit comments