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,7 +32,7 @@ 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 > > ,
@@ -66,23 +66,54 @@ impl OCIUnpacker {
66
66
}
67
67
}
68
68
69
+ // If output ends with path separator, then it is a dir.
70
+ // Output a tar file first, and untar it later.
71
+ fn get_unpack_path ( bootstrap : PathBuf , output : PathBuf ) -> Result < ( bool , PathBuf ) > {
72
+ let is_dir = output
73
+ . to_string_lossy ( )
74
+ . ends_with ( std:: path:: MAIN_SEPARATOR ) ;
75
+ let mut tar_path = output. clone ( ) ;
76
+ if is_dir {
77
+ if !output. exists ( ) {
78
+ create_dir_all ( & output) ?;
79
+ }
80
+ let mut base_name = output. as_os_str ( ) . to_os_string ( ) ;
81
+ base_name. push ( bootstrap. file_name ( ) . unwrap_or_default ( ) ) ;
82
+ base_name. push ( ".tar" ) ;
83
+ tar_path = PathBuf :: from ( base_name) ;
84
+ }
85
+
86
+ Ok ( ( is_dir, tar_path) )
87
+ }
88
+
69
89
impl Unpacker for OCIUnpacker {
70
90
fn unpack ( & self , config : Arc < ConfigV2 > ) -> Result < ( ) > {
71
91
debug ! (
72
- "oci unpacker, bootstrap file: {:?}, output file : {:?}" ,
92
+ "oci unpacker, bootstrap file: {:?}, output path : {:?}" ,
73
93
self . bootstrap, self . output
74
94
) ;
75
95
76
96
let rafs = self . load_rafs ( config) ?;
77
97
98
+ let ( is_dir, tar_path) = get_unpack_path ( self . bootstrap . clone ( ) , self . output . clone ( ) ) ?;
99
+
78
100
let mut builder = self
79
101
. builder_factory
80
- . create ( & rafs, & self . blob_backend , & self . output ) ?;
102
+ . create ( & rafs, & self . blob_backend , & tar_path ) ?;
81
103
82
104
for ( node, path) in RafsIterator :: new ( & rafs) {
83
105
builder. append ( node, & path) ?;
84
106
}
85
107
108
+ // untar this tar file to self.output dir
109
+ if is_dir {
110
+ let file = File :: open ( & tar_path) ?;
111
+ let mut tar = Archive :: new ( file) ;
112
+ tar. unpack ( & self . output ) ?;
113
+ remove_file ( & tar_path) ?;
114
+ }
115
+
116
+ info ! ( "successfully unpack image to: {}" , self . output. display( ) ) ;
86
117
Ok ( ( ) )
87
118
}
88
119
}
@@ -230,3 +261,34 @@ impl TarBuilder for OCITarBuilder {
230
261
bail ! ( "node {:?} can not be unpacked" , path)
231
262
}
232
263
}
264
+
265
+ #[ cfg( test) ]
266
+ mod tests {
267
+ use super :: * ;
268
+ #[ test]
269
+ fn test_get_unpack_path ( ) {
270
+ let bootstrap = PathBuf :: from ( "bootstrap" ) ;
271
+ let output = PathBuf :: from ( "target.tar" ) ;
272
+ let ( is_dir, tar_path) = get_unpack_path ( bootstrap, output) . unwrap ( ) ;
273
+ assert ! ( !is_dir) ;
274
+ assert_eq ! ( tar_path, PathBuf :: from( "target.tar" ) ) ;
275
+
276
+ let bootstrap = PathBuf :: from ( "test/bootstrap" ) ;
277
+ let output = PathBuf :: from ( "target/" ) ;
278
+ let ( is_dir, tar_path) = get_unpack_path ( bootstrap, output) . unwrap ( ) ;
279
+ assert ! ( is_dir) ;
280
+ assert_eq ! ( tar_path, PathBuf :: from( "target/bootstrap.tar" ) ) ;
281
+
282
+ let bootstrap = PathBuf :: from ( "/run/test/bootstrap.meta" ) ;
283
+ let output = PathBuf :: from ( "target/" ) ;
284
+ let ( is_dir, tar_path) = get_unpack_path ( bootstrap, output) . unwrap ( ) ;
285
+ assert ! ( is_dir) ;
286
+ assert_eq ! ( tar_path, PathBuf :: from( "target/bootstrap.meta.tar" ) ) ;
287
+
288
+ let bootstrap = PathBuf :: from ( "/run/test/bootstrap.meta" ) ;
289
+ let output = PathBuf :: from ( "/run/" ) ;
290
+ let ( is_dir, tar_path) = get_unpack_path ( bootstrap, output) . unwrap ( ) ;
291
+ assert ! ( is_dir) ;
292
+ assert_eq ! ( tar_path, PathBuf :: from( "/run/bootstrap.meta.tar" ) ) ;
293
+ }
294
+ }
0 commit comments