@@ -15,15 +15,21 @@ modes iterate over the `kernel-modules` and load them from that path with `modpr
15
15
#[ macro_use]
16
16
extern crate log;
17
17
18
+ use std:: io;
19
+ use std:: {
20
+ collections:: HashMap ,
21
+ ffi:: OsStr ,
22
+ fs,
23
+ path:: { Path , PathBuf } ,
24
+ process:: { self , Command } ,
25
+ } ;
26
+ use tempfile:: NamedTempFile ;
27
+
18
28
use argh:: FromArgs ;
29
+ use early_boot_config_provider:: compression:: OptionalCompressionReader ;
19
30
use serde:: Deserialize ;
20
31
use simplelog:: { Config as LogConfig , LevelFilter , SimpleLogger } ;
21
32
use snafu:: { ensure, OptionExt , ResultExt } ;
22
- use std:: collections:: HashMap ;
23
- use std:: ffi:: OsStr ;
24
- use std:: fs;
25
- use std:: path:: { Path , PathBuf } ;
26
- use std:: process:: { self , Command } ;
27
33
28
34
/// Path to the drivers configuration to use
29
35
const DEFAULT_DRIVER_CONFIG_PATH : & str = "/etc/drivers/" ;
@@ -81,6 +87,56 @@ struct LinkModulesArgs {}
81
87
#[ argh( subcommand, name = "load-modules" ) ]
82
88
struct LoadModulesArgs { }
83
89
90
+ /// Checks if there is a compressed file at the path provided and if so, provides the path with the extension
91
+ pub ( crate ) fn is_compressed ( file_path : & Path ) -> Option < PathBuf > {
92
+ if !file_path. exists ( ) {
93
+ let compressed_path = file_path. with_extension ( "o.gz" ) ;
94
+ if compressed_path. exists ( ) {
95
+ return Some ( compressed_path) ;
96
+ }
97
+ }
98
+ None
99
+ }
100
+
101
+ /// Copies a file to the destination, decompressing if necessary.
102
+ ///
103
+ /// Uses OptionalCompressionReader to automatically detect and decompress
104
+ /// gzip-compressed files. The original file is left intact.
105
+ fn copy_and_decompress ( source_path : & Path , destination : & Path ) -> Result < ( ) > {
106
+ let source_file =
107
+ fs:: File :: open ( source_path) . context ( error:: OpenFileSnafu { path : source_path } ) ?;
108
+
109
+ let mut reader = OptionalCompressionReader :: new ( source_file) ;
110
+
111
+ // Create a temporary file in the destination directory
112
+ let destination_dir = destination
113
+ . parent ( )
114
+ . ok_or_else ( || error:: Error :: InvalidFileName {
115
+ path : destination. to_path_buf ( ) ,
116
+ } ) ?;
117
+
118
+ let mut temp_file = NamedTempFile :: new_in ( destination_dir)
119
+ . context ( error:: CreateFileSnafu { path : destination } ) ?;
120
+
121
+ io:: copy ( & mut reader, & mut temp_file) . context ( error:: DecompressSnafu {
122
+ from : source_path,
123
+ to : destination,
124
+ } ) ?;
125
+
126
+ // Atomically move the temporary file to the final destination
127
+ temp_file
128
+ . persist ( destination)
129
+ . context ( error:: PersistTempFileSnafu { path : destination } ) ?;
130
+
131
+ info ! (
132
+ "Copied and decompressed {} to {}" ,
133
+ source_path. display( ) ,
134
+ destination. display( )
135
+ ) ;
136
+
137
+ Ok ( ( ) )
138
+ }
139
+
84
140
#[ derive( Deserialize , Debug ) ]
85
141
#[ serde( untagged) ]
86
142
/// Enum to hold the two types of configurations supported
@@ -219,10 +275,16 @@ where
219
275
let object_file_path = build_dir. join ( object_file) ;
220
276
if !object_file_path. exists ( ) {
221
277
let from = driver_path. join ( object_file) ;
222
- fs:: copy ( & from, & object_file_path) . context ( error:: CopySnafu {
223
- from : & from,
224
- to : & object_file_path,
225
- } ) ?;
278
+ match is_compressed ( & from) {
279
+ Some ( compressed_path) => {
280
+ info ! ( "Found compressed file: {:?}" , compressed_path) ;
281
+ copy_and_decompress ( & compressed_path, & object_file_path) ?;
282
+ }
283
+ None => {
284
+ info ! ( "Copying {:?}" , & from) ;
285
+ copy_and_decompress ( & from, & object_file_path) ?;
286
+ }
287
+ }
226
288
}
227
289
dependencies_paths. push ( object_file_path. to_string_lossy ( ) . into_owned ( ) ) ;
228
290
}
@@ -271,7 +333,7 @@ where
271
333
. to_string_lossy ( )
272
334
. into_owned ( ) ;
273
335
// Paths to the dependencies for this object file
274
- let mut dependencies = object_file
336
+ let mut dependencies: Vec < String > = object_file
275
337
. link_objects
276
338
. iter ( )
277
339
. map ( |d| {
@@ -282,6 +344,20 @@ where
282
344
} )
283
345
. collect ( ) ;
284
346
347
+ for dependency in & mut dependencies {
348
+ let dep_path = PathBuf :: from ( dependency. as_str ( ) ) ;
349
+ if let Some ( compressed_path) = is_compressed ( & dep_path) {
350
+ let uncompressed = build_dir. join (
351
+ dep_path
352
+ . file_name ( )
353
+ . context ( error:: InvalidFileNameSnafu { path : & dep_path } ) ?,
354
+ ) ;
355
+ info ! ( "Decompressing to {:?}" , & uncompressed) ;
356
+ copy_and_decompress ( & compressed_path, & uncompressed) ?;
357
+ * dependency = uncompressed. to_string_lossy ( ) . into_owned ( ) ;
358
+ }
359
+ }
360
+
285
361
// Link the object file
286
362
let mut args = vec ! [ "-r" . to_string( ) , "-o" . to_string( ) , object_path. clone( ) ] ;
287
363
args. append ( & mut dependencies) ;
@@ -468,10 +544,13 @@ fn main() {
468
544
}
469
545
470
546
/// <コ:ミ くコ:彡 <コ:ミ くコ:彡 <コ:ミ くコ:彡 <コ:ミ くコ:彡 <コ:ミ くコ:彡 <コ:ミ くコ:彡
547
+ /// Error types for driverdog operations
471
548
mod error {
472
549
use snafu:: Snafu ;
473
- use std:: path:: PathBuf ;
474
- use std:: process:: { Command , Output } ;
550
+ use std:: {
551
+ path:: PathBuf ,
552
+ process:: { Command , Output } ,
553
+ } ;
475
554
476
555
#[ derive( Debug , Snafu ) ]
477
556
#[ snafu( visibility( pub ( super ) ) ) ]
@@ -487,6 +566,24 @@ mod error {
487
566
source : std:: io:: Error ,
488
567
} ,
489
568
569
+ #[ snafu( display( "Failed to create file '{}': {}" , path. display( ) , source) ) ]
570
+ CreateFile {
571
+ path : PathBuf ,
572
+ source : std:: io:: Error ,
573
+ } ,
574
+
575
+ #[ snafu( display(
576
+ "Failed to decompress '{}' to '{}': {}" ,
577
+ from. display( ) ,
578
+ to. display( ) ,
579
+ source
580
+ ) ) ]
581
+ Decompress {
582
+ from : PathBuf ,
583
+ to : PathBuf ,
584
+ source : std:: io:: Error ,
585
+ } ,
586
+
490
587
#[ snafu( display( "Failed to deserialize '{}': {}" , path. display( ) , source) ) ]
491
588
Deserialize {
492
589
path : PathBuf ,
@@ -499,6 +596,16 @@ mod error {
499
596
source : std:: io:: Error ,
500
597
} ,
501
598
599
+ #[ snafu( display( "Path '{}' has no filename" , path. display( ) ) ) ]
600
+ InvalidFileName { path : PathBuf } ,
601
+
602
+ /// Failed to move temporary file to final destination.
603
+ #[ snafu( display( "Failed to move temporary file to {}: {}" , path. display( ) , source) ) ]
604
+ PersistTempFile {
605
+ path : PathBuf ,
606
+ source : tempfile:: PersistError ,
607
+ } ,
608
+
502
609
#[ snafu( display( "Module path '{}' is not UTF-8" , path. display( ) ) ) ]
503
610
InvalidModulePath { path : PathBuf } ,
504
611
@@ -514,6 +621,12 @@ mod error {
514
621
source : std:: io:: Error ,
515
622
} ,
516
623
624
+ #[ snafu( display( "Failed to open file '{}': {}" , path. display( ) , source) ) ]
625
+ OpenFile {
626
+ path : PathBuf ,
627
+ source : std:: io:: Error ,
628
+ } ,
629
+
517
630
#[ snafu( display( "Failed to create temporary directory: {}" , source) ) ]
518
631
TmpDir { source : std:: io:: Error } ,
519
632
}
@@ -524,13 +637,52 @@ type Result<T> = std::result::Result<T, error::Error>;
524
637
#[ cfg( test) ]
525
638
mod test {
526
639
use super :: * ;
640
+ use std:: fs:: File ;
527
641
use std:: path:: PathBuf ;
642
+ use tempfile:: TempDir ;
528
643
use walkdir:: WalkDir ;
529
644
530
645
fn test_data ( ) -> PathBuf {
531
646
PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "src/tests" )
532
647
}
533
648
649
+ fn setup_compression_test_files ( ) -> TempDir {
650
+ let temp_dir = tempfile:: tempdir ( ) . expect ( "Failed to create temp directory" ) ;
651
+
652
+ let regular_file_path = temp_dir. path ( ) . join ( "module.o" ) ;
653
+ File :: create ( & regular_file_path) . expect ( "Failed to create regular file" ) ;
654
+
655
+ let compressed_file_path = temp_dir. path ( ) . join ( "compressed_module.o.gz" ) ;
656
+ File :: create ( & compressed_file_path) . expect ( "Failed to create compressed file" ) ;
657
+ temp_dir
658
+ }
659
+
660
+ #[ test]
661
+ fn test_is_compressed_with_existing_file ( ) {
662
+ let temp_dir = setup_compression_test_files ( ) ;
663
+ let regular_file_path = temp_dir. path ( ) . join ( "module.o" ) ;
664
+ let result = is_compressed ( & regular_file_path) ;
665
+ assert ! ( result. is_none( ) ) ;
666
+ }
667
+
668
+ #[ test]
669
+ fn test_is_compressed_with_compressed_alternative ( ) {
670
+ let temp_dir = setup_compression_test_files ( ) ;
671
+ let non_existent_path = temp_dir. path ( ) . join ( "compressed_module.o" ) ;
672
+ let compressed_path = temp_dir. path ( ) . join ( "compressed_module.o.gz" ) ;
673
+ let result = is_compressed ( & non_existent_path) ;
674
+ assert ! ( result. is_some( ) ) ;
675
+ assert_eq ! ( result. unwrap( ) , compressed_path) ;
676
+ }
677
+
678
+ #[ test]
679
+ fn test_is_compressed_with_no_alternative ( ) {
680
+ let temp_dir = setup_compression_test_files ( ) ;
681
+ let non_existent_path = temp_dir. path ( ) . join ( "nonexistent.o" ) ;
682
+ let result = is_compressed ( & non_existent_path) ;
683
+ assert ! ( result. is_none( ) ) ;
684
+ }
685
+
534
686
#[ test]
535
687
fn parse_linking_config ( ) {
536
688
let driver_config_path = test_data ( ) ;
0 commit comments