Skip to content

Commit 73b6718

Browse files
committed
nydus-image: enhance unpack command to support extracting files to dir
When --output is specified as a directory, the nydus-image unpack command will return an error, which is not convenient for users. This patch enhances this command to allow --output to be passed into a dir. In this case, nydus-image will extract all files into this dir. Signed-off-by: Qinqi Qu <[email protected]>
1 parent c9fbce8 commit 73b6718

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

src/bin/nydus-image/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ fn prepare_cmd_args(bti_string: &'static str) -> App {
720720
.arg(
721721
Arg::new("output")
722722
.long("output")
723-
.help("path for output tar file")
723+
.help("Path for output tar file, If it is a directory, extract all files to there.")
724724
.required(true),
725725
),
726726
)
@@ -1365,9 +1365,9 @@ impl Command {
13651365
};
13661366

13671367
OCIUnpacker::new(bootstrap, backend, output)
1368-
.with_context(|| "fail to create unpacker")?
1368+
.with_context(|| "failed to create unpacker")?
13691369
.unpack(config)
1370-
.with_context(|| "fail to unpack")
1370+
.with_context(|| format!("failed to unpack image to: {}", output))
13711371
}
13721372

13731373
fn check(matches: &ArgMatches, build_info: &BuildTimeInfo) -> Result<()> {

src/bin/nydus-image/unpack/mod.rs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: Apache-2.0
55
use std::collections::HashMap;
6-
use std::fs::{File, OpenOptions};
6+
use std::fs::{create_dir_all, remove_file, File, OpenOptions};
77
use std::io::Read;
88
use std::path::{Path, PathBuf};
99
use std::rc::Rc;
@@ -18,7 +18,7 @@ use nydus_rafs::{
1818
};
1919
use nydus_storage::backend::BlobBackend;
2020
use nydus_storage::device::BlobInfo;
21-
use tar::{Builder, Header};
21+
use tar::{Archive, Builder, Header};
2222

2323
use self::pax::{
2424
OCIBlockBuilder, OCICharBuilder, OCIDirBuilder, OCIFifoBuilder, OCILinkBuilder, OCIRegBuilder,
@@ -32,7 +32,7 @@ pub trait Unpacker {
3232
fn unpack(&self, config: Arc<ConfigV2>) -> Result<()>;
3333
}
3434

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.
3636
pub struct OCIUnpacker {
3737
bootstrap: PathBuf,
3838
blob_backend: Option<Arc<dyn BlobBackend + Send + Sync>>,
@@ -66,23 +66,54 @@ impl OCIUnpacker {
6666
}
6767
}
6868

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+
6989
impl Unpacker for OCIUnpacker {
7090
fn unpack(&self, config: Arc<ConfigV2>) -> Result<()> {
7191
debug!(
72-
"oci unpacker, bootstrap file: {:?}, output file: {:?}",
92+
"oci unpacker, bootstrap file: {:?}, output path: {:?}",
7393
self.bootstrap, self.output
7494
);
7595

7696
let rafs = self.load_rafs(config)?;
7797

98+
let (is_dir, tar_path) = get_unpack_path(self.bootstrap.clone(), self.output.clone())?;
99+
78100
let mut builder = self
79101
.builder_factory
80-
.create(&rafs, &self.blob_backend, &self.output)?;
102+
.create(&rafs, &self.blob_backend, &tar_path)?;
81103

82104
for (node, path) in RafsIterator::new(&rafs) {
83105
builder.append(node, &path)?;
84106
}
85107

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());
86117
Ok(())
87118
}
88119
}
@@ -230,3 +261,34 @@ impl TarBuilder for OCITarBuilder {
230261
bail!("node {:?} can not be unpacked", path)
231262
}
232263
}
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

Comments
 (0)