1313use std:: {
1414 fmt:: { self as std_fmt, Write as std_write} ,
1515 fs:: { File , OpenOptions } ,
16- io:: { self , Error , ErrorKind , Write } ,
16+ io:: { self , ErrorKind , Write } ,
1717 os:: unix:: fs:: MetadataExt ,
1818 path:: Path ,
1919 time:: { Duration , Instant , SystemTime , UNIX_EPOCH } ,
@@ -81,10 +81,10 @@ impl Visit for StringVisitor<'_> {
8181 /// * `value` - The debug value associated with the field.
8282 fn record_debug (
8383 & mut self ,
84- _field : & tracing:: field:: Field ,
84+ field : & tracing:: field:: Field ,
8585 value : & dyn std_fmt:: Debug ,
8686 ) {
87- write ! ( self . string, "{}={:?}; " , _field . name( ) , value)
87+ write ! ( self . string, "{}={:?}; " , field . name( ) , value)
8888 . expect ( "Writing to a string should never fail" ) ;
8989 }
9090}
@@ -134,7 +134,7 @@ impl EmitKVPLayer {
134134 /// # Arguments
135135 /// * `file_path` - The file path where the KVP data will be stored.
136136 ///
137- pub fn new ( file_path : std:: path:: PathBuf ) -> Result < Self , std :: io :: Error > {
137+ pub fn new ( file_path : std:: path:: PathBuf ) -> Result < Self , anyhow :: Error > {
138138 truncate_guest_pool_file ( & file_path) ?;
139139
140140 let file = OpenOptions :: new ( )
@@ -309,21 +309,11 @@ where
309309 let start_time =
310310 end_time. checked_sub ( elapsed) . unwrap_or ( UNIX_EPOCH ) ;
311311
312- let start_time_dt = DateTime :: < Utc > :: from (
313- UNIX_EPOCH
314- + start_time
315- . duration_since ( UNIX_EPOCH )
316- . unwrap_or_default ( ) ,
317- )
318- . format ( "%Y-%m-%dT%H:%M:%S%.3fZ" ) ;
312+ let start_time_dt = DateTime :: < Utc > :: from ( start_time)
313+ . format ( "%Y-%m-%dT%H:%M:%S%.3fZ" ) ;
319314
320- let end_time_dt = DateTime :: < Utc > :: from (
321- UNIX_EPOCH
322- + end_time
323- . duration_since ( UNIX_EPOCH )
324- . unwrap_or_default ( ) ,
325- )
326- . format ( "%Y-%m-%dT%H:%M:%S%.3fZ" ) ;
315+ let end_time_dt = DateTime :: < Utc > :: from ( end_time)
316+ . format ( "%Y-%m-%dT%H:%M:%S%.3fZ" ) ;
327317
328318 let event_value = format ! (
329319 "Start: {} | End: {} | Status: {}" ,
@@ -362,41 +352,51 @@ fn generate_event_key(
362352/// exceeds the allowed size, it is split into multiple slices for encoding.
363353/// This is used for logging events to a KVP file.
364354///
355+ /// # Note
356+ /// - The key must be zero-padded to `HV_KVP_EXCHANGE_MAX_KEY_SIZE` to meet Hyper-V's expected formatting.
357+ ///
365358/// # Arguments
366359/// * `key` - The key as a string slice.
367360/// * `value` - The value associated with the key.
368361fn encode_kvp_item ( key : & str , value : & str ) -> Vec < Vec < u8 > > {
369- let key_bytes = key. as_bytes ( ) ;
370- let value_bytes = value. as_bytes ( ) ;
371-
372- let key_len = key_bytes. len ( ) . min ( HV_KVP_EXCHANGE_MAX_KEY_SIZE ) ;
373- let mut key_buf = vec ! [ 0u8 ; HV_KVP_EXCHANGE_MAX_KEY_SIZE ] ;
374- key_buf[ ..key_len] . copy_from_slice ( & key_bytes[ ..key_len] ) ;
375-
376- let mut kvp_slices = Vec :: new ( ) ;
362+ let key_buf = key
363+ . as_bytes ( )
364+ . iter ( )
365+ . take ( HV_KVP_EXCHANGE_MAX_KEY_SIZE )
366+ . chain (
367+ vec ! [ 0_u8 ; HV_KVP_EXCHANGE_MAX_KEY_SIZE . saturating_sub( key. len( ) ) ]
368+ . iter ( ) ,
369+ )
370+ . copied ( )
371+ . collect :: < Vec < _ > > ( ) ;
372+
373+ debug_assert ! ( key_buf. len( ) == HV_KVP_EXCHANGE_MAX_KEY_SIZE ) ;
374+
375+ let kvp_slices = value
376+ . as_bytes ( )
377+ . chunks ( HV_KVP_AZURE_MAX_VALUE_SIZE )
378+ . map ( |chunk| {
379+ let mut buffer = Vec :: with_capacity (
380+ HV_KVP_EXCHANGE_MAX_KEY_SIZE + HV_KVP_EXCHANGE_MAX_VALUE_SIZE ,
381+ ) ;
382+ buffer. extend_from_slice ( & key_buf) ;
383+ buffer. extend_from_slice ( chunk) ;
384+ while buffer. len ( )
385+ < HV_KVP_EXCHANGE_MAX_KEY_SIZE + HV_KVP_EXCHANGE_MAX_VALUE_SIZE
386+ {
387+ buffer. push ( 0 ) ;
388+ }
377389
378- for chunk in value_bytes . chunks ( HV_KVP_AZURE_MAX_VALUE_SIZE ) {
379- let mut value_buf = vec ! [ 0u8 ; HV_KVP_EXCHANGE_MAX_VALUE_SIZE ] ;
380- value_buf [ ..chunk . len ( ) ] . copy_from_slice ( chunk ) ;
390+ buffer
391+ } )
392+ . collect :: < Vec < Vec < u8 > > > ( ) ;
381393
382- kvp_slices. push ( encode_kvp_slice ( key_buf . clone ( ) , value_buf ) ) ;
383- }
394+ debug_assert ! ( kvp_slices. iter ( ) . all ( |kvp| kvp . len ( )
395+ == HV_KVP_EXCHANGE_MAX_KEY_SIZE + HV_KVP_EXCHANGE_MAX_VALUE_SIZE ) ) ;
384396
385397 kvp_slices
386398}
387399
388- /// Combines the key and value of a KVP into a single byte slice, ensuring
389- /// proper formatting for consumption by hv_kvp_daemon service,
390- /// which typically reads from /var/lib/hyperv/.kvp_pool_1.
391- fn encode_kvp_slice ( key : Vec < u8 > , value : Vec < u8 > ) -> Vec < u8 > {
392- let mut buffer = Vec :: with_capacity (
393- HV_KVP_EXCHANGE_MAX_KEY_SIZE + HV_KVP_EXCHANGE_MAX_VALUE_SIZE ,
394- ) ;
395- buffer. extend_from_slice ( & key) ;
396- buffer. extend_from_slice ( & value) ;
397- buffer
398- }
399-
400400/// Decodes a KVP byte slice into its corresponding key and value strings.
401401/// This is useful for inspecting or logging raw KVP data.
402402#[ cfg( test) ]
@@ -412,18 +412,23 @@ pub fn decode_kvp_item(
412412 }
413413
414414 let key = String :: from_utf8 (
415- record_data[ 0 ..HV_KVP_EXCHANGE_MAX_KEY_SIZE ] . to_vec ( ) ,
415+ record_data
416+ . iter ( )
417+ . take ( HV_KVP_EXCHANGE_MAX_KEY_SIZE )
418+ . cloned ( )
419+ . collect :: < Vec < _ > > ( ) ,
416420 )
417421 . unwrap_or_else ( |_| String :: new ( ) )
418422 . trim_end_matches ( '\x00' )
419423 . to_string ( ) ;
420424
421425 let value = String :: from_utf8 (
422- record_data[ HV_KVP_EXCHANGE_MAX_KEY_SIZE .. ]
426+ record_data
423427 . iter ( )
428+ . skip ( HV_KVP_EXCHANGE_MAX_KEY_SIZE )
424429 . take ( HV_KVP_AZURE_MAX_VALUE_SIZE )
425430 . cloned ( )
426- . collect ( ) ,
431+ . collect :: < Vec < _ > > ( ) ,
427432 )
428433 . unwrap_or_else ( |_| String :: new ( ) )
429434 . trim_end_matches ( '\x00' )
@@ -435,12 +440,9 @@ pub fn decode_kvp_item(
435440/// Truncates the guest pool KVP file if it contains stale data (i.e., data
436441/// older than the system's boot time). Logs whether the file was truncated
437442/// or no action was needed.
438- fn truncate_guest_pool_file ( kvp_file : & Path ) -> Result < ( ) , Error > {
439- let boot_time = SystemTime :: now ( )
440- . duration_since ( UNIX_EPOCH )
441- . map_err ( |e| Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?
442- . as_secs ( )
443- - get_uptime ( ) ?. as_secs ( ) ;
443+ fn truncate_guest_pool_file ( kvp_file : & Path ) -> Result < ( ) , anyhow:: Error > {
444+ let boot_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) ?. as_secs ( )
445+ - get_uptime ( ) . as_secs ( ) ;
444446
445447 match kvp_file. metadata ( ) {
446448 Ok ( metadata) => {
@@ -461,7 +463,8 @@ fn truncate_guest_pool_file(kvp_file: &Path) -> Result<(), Error> {
461463 return Ok ( ( ) ) ;
462464 }
463465 Err ( e) => {
464- return Err ( e) ;
466+ return Err ( anyhow:: Error :: from ( e)
467+ . context ( "Failed to access file metadata" ) ) ;
465468 }
466469 }
467470
@@ -471,12 +474,12 @@ fn truncate_guest_pool_file(kvp_file: &Path) -> Result<(), Error> {
471474/// Retrieves the system's uptime using the `sysinfo` crate, returning the duration
472475/// since the system booted. This can be useful for time-based calculations or checks,
473476/// such as determining whether data is stale or calculating the approximate boot time.
474- fn get_uptime ( ) -> Result < Duration , Error > {
477+ fn get_uptime ( ) -> Duration {
475478 let mut system = System :: new ( ) ;
476479 system. refresh_system ( ) ;
477480
478481 let uptime_seconds = system. uptime ( ) ;
479- Ok ( Duration :: from_secs ( uptime_seconds) )
482+ Duration :: from_secs ( uptime_seconds)
480483}
481484
482485#[ cfg( test) ]
0 commit comments