3
3
//
4
4
// SPDX-License-Identifier: Apache-2.0
5
5
use std:: collections:: HashMap ;
6
- use std:: fs:: { File , OpenOptions } ;
6
+ use std:: fs:: { create_dir_all , remove_file , File , OpenOptions } ;
7
7
use std:: io:: Read ;
8
8
use std:: path:: { Path , PathBuf } ;
9
9
use std:: rc:: Rc ;
@@ -18,7 +18,7 @@ use nydus_rafs::{
18
18
} ;
19
19
use nydus_storage:: backend:: BlobBackend ;
20
20
use nydus_storage:: device:: BlobInfo ;
21
- use tar:: { Builder , Header } ;
21
+ use tar:: { Archive , Builder , Header } ;
22
22
23
23
use self :: pax:: {
24
24
OCIBlockBuilder , OCICharBuilder , OCIDirBuilder , OCIFifoBuilder , OCILinkBuilder , OCIRegBuilder ,
@@ -32,11 +32,12 @@ pub trait Unpacker {
32
32
fn unpack ( & self , config : Arc < ConfigV2 > ) -> Result < ( ) > ;
33
33
}
34
34
35
- /// A unpacker with the ability to convert bootstrap file and blob file to tar
35
+ /// A unpacker with the ability to convert bootstrap file and blob file to tar or dir.
36
36
pub struct OCIUnpacker {
37
37
bootstrap : PathBuf ,
38
38
blob_backend : Option < Arc < dyn BlobBackend + Send + Sync > > ,
39
39
output : PathBuf ,
40
+ untar : bool ,
40
41
41
42
builder_factory : OCITarBuilderFactory ,
42
43
}
@@ -46,6 +47,7 @@ impl OCIUnpacker {
46
47
bootstrap : & Path ,
47
48
blob_backend : Option < Arc < dyn BlobBackend + Send + Sync > > ,
48
49
output : & str ,
50
+ untar : bool ,
49
51
) -> Result < Self > {
50
52
let bootstrap = bootstrap. to_path_buf ( ) ;
51
53
let output = PathBuf :: from ( output) ;
@@ -57,31 +59,72 @@ impl OCIUnpacker {
57
59
bootstrap,
58
60
blob_backend,
59
61
output,
62
+ untar,
60
63
} )
61
64
}
62
65
63
66
fn load_rafs ( & self , config : Arc < ConfigV2 > ) -> Result < RafsSuper > {
64
67
let ( rs, _) = RafsSuper :: load_from_file ( self . bootstrap . as_path ( ) , config, false ) ?;
65
68
Ok ( rs)
66
69
}
70
+
71
+ fn get_unpack_path ( & self ) -> Result < PathBuf > {
72
+ // If output ends with path separator, then it is a dir.
73
+ let is_dir = self
74
+ . output
75
+ . to_string_lossy ( )
76
+ . ends_with ( std:: path:: MAIN_SEPARATOR ) ;
77
+
78
+ // Unpack the tar file to a subdirectory
79
+ if is_dir || self . untar {
80
+ if !self . output . exists ( ) {
81
+ create_dir_all ( & self . output ) ?;
82
+ }
83
+ let tar_path = self
84
+ . output
85
+ . join ( self . bootstrap . file_stem ( ) . unwrap_or_default ( ) )
86
+ . with_extension ( "tar" ) ;
87
+
88
+ return Ok ( tar_path) ;
89
+ }
90
+
91
+ // Unpack the tar file to the specified location
92
+ Ok ( self . output . clone ( ) )
93
+ }
67
94
}
68
95
69
96
impl Unpacker for OCIUnpacker {
70
97
fn unpack ( & self , config : Arc < ConfigV2 > ) -> Result < ( ) > {
71
98
debug ! (
72
- "oci unpacker, bootstrap file: {:?}, output file : {:?}" ,
99
+ "oci unpacker, bootstrap file: {:?}, output path : {:?}" ,
73
100
self . bootstrap, self . output
74
101
) ;
75
102
76
103
let rafs = self . load_rafs ( config) ?;
77
104
105
+ let tar_path = self . get_unpack_path ( ) ?;
78
106
let mut builder = self
79
107
. builder_factory
80
- . create ( & rafs, & self . blob_backend , & self . output ) ?;
108
+ . create ( & rafs, & self . blob_backend , & tar_path ) ?;
81
109
82
110
for ( node, path) in RafsIterator :: new ( & rafs) {
83
111
builder. append ( node, & path) ?;
84
112
}
113
+ info ! ( "successfully unpack image to: {}" , tar_path. display( ) ) ;
114
+
115
+ // untar this tar file to self.output dir
116
+ if self . untar {
117
+ let file = File :: open ( & tar_path) ?;
118
+ let mut tar = Archive :: new ( file) ;
119
+ tar. unpack ( & self . output ) ?;
120
+ remove_file ( & tar_path) ?;
121
+
122
+ info ! (
123
+ "successfully untar {} to: {}" ,
124
+ tar_path. display( ) ,
125
+ self . output. display( )
126
+ ) ;
127
+ }
85
128
86
129
Ok ( ( ) )
87
130
}
@@ -230,3 +273,32 @@ impl TarBuilder for OCITarBuilder {
230
273
bail ! ( "node {:?} can not be unpacked" , path)
231
274
}
232
275
}
276
+
277
+ #[ cfg( test) ]
278
+ mod tests {
279
+ use super :: * ;
280
+ #[ test]
281
+ fn test_get_unpack_path ( ) {
282
+ // test data: (bootstrap, output, untar, expected_tar_path)
283
+ let test_cases = [
284
+ ( "./test" , "target.tar" , false , "target.tar" ) ,
285
+ ( "test/test" , "target" , false , "target" ) ,
286
+ ( "test/test" , "target/" , false , "target/test.tar" ) ,
287
+ ( "/run/test.meta" , "target/" , false , "target/test.tar" ) ,
288
+ ( "/run/test.meta" , "/run/" , false , "/run/test.tar" ) ,
289
+ ( "./test" , "target.tar" , true , "target.tar/test.tar" ) ,
290
+ ( "test/test" , "target" , true , "target/test.tar" ) ,
291
+ ( "test/test" , "target/" , true , "target/test.tar" ) ,
292
+ ] ;
293
+
294
+ for ( bootstrap, output, untar, expected_tar_path) in test_cases {
295
+ let unpacker = OCIUnpacker :: new ( Path :: new ( bootstrap) , None , output, untar) . unwrap ( ) ;
296
+ let tar_path = unpacker. get_unpack_path ( ) . unwrap ( ) ;
297
+ assert_eq ! (
298
+ tar_path,
299
+ PathBuf :: from( expected_tar_path) ,
300
+ "tar_path not equal to expected_tar_path"
301
+ ) ;
302
+ }
303
+ }
304
+ }
0 commit comments