diff options
-rw-r--r-- | src/bin/geniza-drive.rs | 17 | ||||
-rw-r--r-- | src/drive.rs | 34 |
2 files changed, 44 insertions, 7 deletions
diff --git a/src/bin/geniza-drive.rs b/src/bin/geniza-drive.rs index 7f945b0..79ba480 100644 --- a/src/bin/geniza-drive.rs +++ b/src/bin/geniza-drive.rs @@ -45,6 +45,12 @@ fn run() -> Result<()> { .arg_from_usage("--target <path> 'path to import the file to (if not top level)'") ) .subcommand( + SubCommand::with_name("export-file") + .about("Copies a file from dat archive to local disk") + .arg_from_usage("<FILE> 'file to export'") + .arg_from_usage("--target <path> 'path to save the file to (if not same name)'") + ) + .subcommand( SubCommand::with_name("log") .about("History of additions/deletions from this dat") ) @@ -61,7 +67,7 @@ fn run() -> Result<()> { let dir = Path::new(matches.value_of("dat-dir").unwrap()); match matches.subcommand() { ("init", Some(_subm)) => { - let drive = DatDrive::create(dir)?; + let _drive = DatDrive::create(dir)?; // TODO: print public key in hex println!("Done!"); } @@ -82,6 +88,15 @@ fn run() -> Result<()> { drive.import_file(&path, &fpath)?; } + ("export-file", Some(subm)) => { + let path = Path::new(subm.value_of("FILE").unwrap()); + let mut drive = DatDrive::open(dir, true)?; + let fpath = match subm.value_of("target") { + None => Path::new("/").join(path.file_name().unwrap()), + Some(p) => Path::new("/").join(p) + }; + drive.export_file(&path, &fpath)?; + } ("cat", Some(subm)) => { let path = Path::new(subm.value_of("FILE").unwrap()); let mut drive = DatDrive::open(dir, true)?; diff --git a/src/drive.rs b/src/drive.rs index 9aa6ea2..740e949 100644 --- a/src/drive.rs +++ b/src/drive.rs @@ -1,8 +1,8 @@ -use std::io::Read; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::os::unix::fs::MetadataExt; -use std::fs::File; +use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; +use std::fs::{File, OpenOptions}; use std::cmp::min; use std::ffi::OsStr; use protobuf::Message; @@ -403,6 +403,7 @@ impl<'a> DatDrive { Ok(children) } + // XXX: needs test /// Copies Stat metadata and all content from a file in the "real" filesystem into the /// DatDrive. pub fn import_file<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, source: P, dest: Q) -> Result<()> { @@ -418,15 +419,36 @@ impl<'a> DatDrive { self.add_file(dest, &mut stat, in_file) } + // XXX: needs test /// Copies a file from the drive to the "real" filesystem, preserving Stat metadata. - pub fn export_file<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, _source: P, _dest: Q) -> Result<()> { - unimplemented!() + pub fn export_file<P: AsRef<Path>, Q: AsRef<Path>>(&mut self, source: P, dest: Q) -> Result<()> { + + let source = source.as_ref(); + let de = self.get_file_entry(source)?; + if let Some(entry) = de { + let stat = entry.stat.unwrap(); + let mut out_file = OpenOptions::new() + .create_new(true) + .write(true) + .mode(stat.get_mode()) + .open(dest)?; + let offset = stat.get_offset(); + let blocks = stat.get_blocks(); + for i in offset..(offset+blocks) { + let chunk = self.content.get_data_entry(i)?; + out_file.write_all(&chunk)?; + } + // TODO: more outfile metadata (uid, guid, etc) + } else { + bail!("Couldn't find path: {}", source.display()); + } + + Ok(()) } pub fn read_file_bytes<P: AsRef<Path>>(&mut self, path: P) -> Result<Vec<u8>> { let de = self.get_file_entry(path.as_ref())?; if let Some(entry) = de { - // XXX: read and concatonate chunks let stat = entry.stat.unwrap(); let mut buf = vec![]; let offset = stat.get_offset(); |