use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use serde_json::Value;
use tar::Archive;
use flate2::read::GzDecoder;
fn check_if_gzipped(file: &mut File) -> std::io::Result<bool> {
let mut magic_number = [0; 2];
file.seek(SeekFrom::Start(0))?;
file.read_exact(&mut magic_number)?;
file.seek(SeekFrom::Start(0))?;
Ok(magic_number == [0x1f, 0x8b])
}
fn read_json_file<P: AsRef<Path>>(path: P) -> std::io::Result<Value> {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(serde_json::from_str(&contents)?)
}
pub fn extract_layers(main_path: &str, unpack_path: &str) -> Result<String, String> {
let manifest_path = std::path::Path::new(main_path).join("manifest.json");
let blobs_dir = std::path::Path::new(main_path);
let unpack_path = std::path::Path::new(unpack_path);
if !unpack_path.exists() {
std::fs::create_dir_all(&unpack_path).map_err(|e| e.to_string())?;
}
let manifest = read_json_file(&manifest_path).map_err(|e| e.to_string())?;
if let Some(layers) = manifest[0]["Layers"].as_array() {
println!("Found {} layers in manifest", layers.len());
for layer in layers {
println!("Extracting layer: {}", layer);
let base_path = blobs_dir;
let layer_path = base_path.join(
match layer.as_str() {
Some(layer) => layer,
None => {
return Err(
"Failed to get the layer path when extracting a layer from the Docker image".to_string()
);
}
});
let mut tar_file = File::open(&layer_path).map_err(|e| e.to_string())?;
let if_gzipped = check_if_gzipped(&mut tar_file).map_err(|e| e.to_string())?;
match if_gzipped {
true => {
println!("Layer is gzipped");
let decompressed = GzDecoder::new(tar_file);
let mut archive = Archive::new(decompressed);
archive.unpack(unpack_path).map_err(|e| e.to_string())?;
},
false => {
println!("Layer is not gzipped");
let mut archive = Archive::new(tar_file);
archive.unpack(unpack_path).map_err(|e| e.to_string())?;
}
}
}
}
else {
println!("No layers found in manifest");
}
Ok(match unpack_path.to_str(){
Some(v) => v.to_string(),
None => {
return Err("Failed to convert path to string when extracting layers from the Docker image".to_string());
}
})
}