1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
use std::fs; use std::io; use std::fs::File; use std::path::{Component, PathBuf, Path}; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use flate2::read::GzDecoder; use tar::Archive; use zip; pub fn extract_zip(filename: &PathBuf, install_dir: &PathBuf) -> Result<(), io::Error> { let file = File::open(filename)?; let mut archive = zip::ZipArchive::new(file)?; for i in 0..archive.len() { let mut file = archive.by_index(i)?; let outpath = sanitize_filename(file.name()); let install_path = Path::new(install_dir.as_path()).join(outpath); println!("{}", install_path.display()); create_directory(install_path.parent().unwrap_or(Path::new("")), None)?; let perms = convert_permissions(file.unix_mode()); if (&*file.name()).ends_with("/") { create_directory(&install_path, perms)?; } else { write_file(&mut file, &install_path, perms)?; } } Ok(()) } pub fn extract_targz(targz: &PathBuf, install_dir: &PathBuf) -> Result<(), io::Error> { let tarball = File::open(targz)?; let gz = GzDecoder::new(tarball)?; let mut tar = Archive::new(gz); tar.unpack(install_dir)?; Ok(()) } fn write_file(file: &mut zip::read::ZipFile, outpath: &Path, perms: Option<fs::Permissions>) -> Result<(), io::Error> { let mut outfile = File::create(&outpath)?; io::copy(file, &mut outfile)?; if let Some(perms) = perms { fs::set_permissions(outpath, perms)?; } Ok(()) } fn create_directory(outpath: &Path, perms: Option<fs::Permissions>) -> Result<(), io::Error> { fs::create_dir_all(&outpath)?; if let Some(perms) = perms { fs::set_permissions(outpath, perms)?; } Ok(()) } #[cfg(unix)] fn convert_permissions(mode: Option<u32>) -> Option<fs::Permissions> { match mode { Some(mode) => Some(fs::Permissions::from_mode(mode)), None => None, } } #[cfg(not(unix))] fn convert_permissions(_mode: Option<u32>) -> Option<fs::Permissions> { None } fn sanitize_filename(filename: &str) -> PathBuf { let no_null_filename = match filename.find('\0') { Some(index) => &filename[0..index], None => filename, }; Path::new(no_null_filename) .components() .filter(|component| *component != Component::ParentDir) .fold(PathBuf::new(), |mut path, ref cur| { path.push(cur.as_os_str()); path }) } #[cfg(test)] mod test { use super::*; use std::env; use tempdir::TempDir; #[test] fn test_zip_extractor() { let pwd = env::current_dir().unwrap(); let zipfile = pwd.join("fixture").join("test-extractor.zip"); let extract_dir = TempDir::new("grnenv-rs").unwrap().into_path(); assert!(extract_zip(&zipfile, &extract_dir).is_ok()); assert!(extract_dir.is_dir()); assert!(extract_dir.join("test-extractor").is_dir()); assert!(extract_dir.join("test-extractor").join("test.txt").exists()); assert!(extract_dir.join("test-extractor").join("nested").is_dir()); assert!(extract_dir.join("test-extractor").join("nested").join("test.txt").exists()); } #[test] fn test_targz_extractor() { let pwd = env::current_dir().unwrap(); let targz = pwd.join("fixture").join("test-extractor.tar.gz"); let extract_dir = TempDir::new("grnenv-rs").unwrap().into_path(); assert!(extract_targz(&targz, &extract_dir).is_ok()); assert!(extract_dir.is_dir()); assert!(extract_dir.join("test-extractor").is_dir()); assert!(extract_dir.join("test-extractor").join("test.txt").exists()); assert!(extract_dir.join("test-extractor").join("nested").is_dir()); assert!(extract_dir.join("test-extractor").join("nested").join("test.txt").exists()); } }