From 97c6ba1d59ed1fabe7048a9132076703c4eef935 Mon Sep 17 00:00:00 2001 From: vjoki Date: Wed, 18 Dec 2019 21:46:34 +0200 Subject: [PATCH 1/7] Remove cstr! macro and use CString --- src/archive.rs | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 1ac1092..c8a387a 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -3,19 +3,11 @@ use regex::Regex; use std::os::raw::c_int; use std::str; use std::fmt; -use std::ffi::CStr; +use std::ffi::{CString, CStr}; use std::iter::repeat; use std::ptr::NonNull; use error::*; -macro_rules! cstr { - ($e:expr) => ({ - let mut owned: String = $e.into(); - owned.push_str("\0"); - owned - }) -} - #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OpenMode { @@ -179,7 +171,12 @@ impl<'a> Archive<'a> { path: Option, operation: Operation) -> UnrarResult { - OpenArchive::new(self.filename, mode, self.password, path, operation) + let path = path.and_then(|x| Some(CString::new(x).unwrap())); + let password = self.password.and_then(|x| Some(CString::new(x).unwrap())); + let filename = CString::new(self.filename) + .map_err(|_| UnrarError::from(Code::Unknown, When::Open))?; + + OpenArchive::new(filename, mode, password, path, operation) } } @@ -187,18 +184,17 @@ impl<'a> Archive<'a> { pub struct OpenArchive { handle: NonNull, operation: Operation, - destination: Option, + destination: Option, damaged: bool, } impl OpenArchive { - fn new(filename: String, + fn new(filename: CString, mode: OpenMode, - password: Option, - destination: Option, + password: Option, + destination: Option, operation: Operation) -> UnrarResult { - let filename = cstr!(filename); let mut data = native::OpenArchiveData::new(filename.as_ptr() as *const _, mode as u32); let handle = NonNull::new(unsafe { native::RAROpenArchive(&mut data as *mut _) } @@ -207,13 +203,12 @@ impl OpenArchive { if let Some(handle) = handle { if let Some(pw) = password { - let pw = cstr!(pw); unsafe { native::RARSetPassword(handle.as_ptr(), pw.as_ptr() as *const _) } } - let dest = destination.map(|path| cstr!(path)); + let archive = OpenArchive { handle: handle, - destination: dest, + destination: destination, damaged: false, operation: operation, }; @@ -245,9 +240,9 @@ impl OpenArchive { match msg { native::UCM_CHANGEVOLUME => { let ptr = p1 as *const _; - let next = str::from_utf8(unsafe { CStr::from_ptr(ptr) }.to_bytes()).unwrap(); - let our_option = unsafe { &mut *(user_data as *mut Option) }; - *our_option = Some(String::from(next)); + let next = unsafe { CStr::from_ptr(ptr) }.to_owned(); + let our_option = unsafe { &mut *(user_data as *mut enum_primitive::Option) }; + *our_option = Some(next); match p2 { // Next volume not found. -1 means stop native::RAR_VOL_ASK => -1, @@ -268,7 +263,7 @@ impl Iterator for OpenArchive { if self.damaged { return None; } - let mut volume = None; + let mut volume: Option = None; unsafe { native::RARSetCallback(self.handle.as_ptr(), Self::callback, &mut volume as *mut _ as native::LPARAM) } @@ -294,7 +289,8 @@ impl Iterator for OpenArchive { let mut entry = Entry::from(header); // EOpen on Process: Next volume not found if process_result == Code::EOpen { - entry.next_volume = volume; + entry.next_volume = volume.as_ref() + .and_then(|x| Some(x.to_str().unwrap().to_owned())); self.damaged = true; Some(Err(UnrarError::new(process_result, When::Process, entry))) } else { @@ -381,10 +377,8 @@ impl fmt::Display for Entry { impl From for Entry { fn from(header: native::HeaderData) -> Self { Entry { - filename: str::from_utf8(unsafe { CStr::from_ptr(header.filename.as_ptr()) } - .to_bytes()) - .unwrap() - .into(), + filename: unsafe { CStr::from_ptr(header.filename.as_ptr()) }.to_str().unwrap() + .to_owned(), flags: EntryFlags::from_bits(header.flags).unwrap(), unpacked_size: header.unp_size, file_crc: header.file_crc, From 924418f785675ebe9befe74f01533565a7656d0b Mon Sep 17 00:00:00 2001 From: vjoki Date: Mon, 25 Nov 2019 19:58:57 +0200 Subject: [PATCH 2/7] Fix failing test list_missing_volumes fails on Windows, because it expects '/' as the path separator. --- tests/multipart.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/multipart.rs b/tests/multipart.rs index 8cf6f0a..e7aa33b 100644 --- a/tests/multipart.rs +++ b/tests/multipart.rs @@ -3,24 +3,25 @@ extern crate unrar; use unrar::Archive; use unrar::error::{Code, When}; +use std::path::PathBuf; use std::io::prelude::*; #[test] fn list_missing_volume() { - let expected = vec!["build.rs", - "Cargo.toml", - "examples/lister.rs", - "src/lib.rs", - "vendor/unrar/acknow.txt", - "vendor/unrar/arccmt.cpp"]; + let expected: Vec = vec!["build.rs", + "Cargo.toml", + "examples/lister.rs", + "src/lib.rs", + "vendor/unrar/acknow.txt", + "vendor/unrar/arccmt.cpp"].iter().map(|x| x.into()).collect(); let mut archive = Archive::new("data/archive.part1.rar".into()).list().unwrap(); for (i, e) in archive.by_ref().enumerate().take(expected.len()) { - assert_eq!(e.unwrap().filename, expected[i]); + assert_eq!(PathBuf::from(e.unwrap().filename), expected[i]); } let err = archive.next().unwrap().err().unwrap(); assert_eq!(err.code, Code::EOpen); assert_eq!(err.when, When::Process); let data = err.data.unwrap(); - assert_eq!(data.filename, "vendor/unrar/archive.cpp"); - assert_eq!(data.next_volume.unwrap(), "data/archive.part2.rar"); + assert_eq!(PathBuf::from(data.filename), PathBuf::from("vendor/unrar/archive.cpp")); + assert_eq!(PathBuf::from(data.next_volume.unwrap()), PathBuf::from("data/archive.part2.rar")); } From 463b0bb2073816a5d0231919eda48b3df21869de Mon Sep 17 00:00:00 2001 From: vjoki Date: Mon, 9 Dec 2019 20:37:24 +0200 Subject: [PATCH 3/7] Add unicode test --- data/unicode.rar | Bin 0 -> 96 bytes tests/utf8.rs | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 data/unicode.rar create mode 100644 tests/utf8.rs diff --git a/data/unicode.rar b/data/unicode.rar new file mode 100644 index 0000000000000000000000000000000000000000..678b269009f8482a5b3e27eb5443c23d40649c12 GIT binary patch literal 96 zcmWGaEK-zWXJjy*wDl<$BP$yND#z{p#Y z`lw;qqlT%)C6DIxa4`$7DSsDh$ZKc#VSZclhxsjAfOOM``K`@dvgKic%&aU70N8CJ A)&Kwi literal 0 HcmV?d00001 diff --git a/tests/utf8.rs b/tests/utf8.rs new file mode 100644 index 0000000..72b8012 --- /dev/null +++ b/tests/utf8.rs @@ -0,0 +1,9 @@ +extern crate unrar; + +use unrar::Archive; + +#[test] +fn unicode_list() { + let mut entries = Archive::new("data/unicode.rar".into()).list().unwrap(); + assert_eq!(entries.next().unwrap().unwrap().filename, "te…―st✌"); +} From 847f9c58451111dc009e59415f9820ba07d86091 Mon Sep 17 00:00:00 2001 From: vjoki Date: Wed, 18 Dec 2019 22:11:51 +0200 Subject: [PATCH 4/7] Rework strings to utilize wchar and the W/Ex API Switch over to widestring and W/Ex variants of the unrar_sys functions everywhere, fixing unicode issues at the cost of using u16/u32 instead of u8 types for strings. Though these are what the underlying unrar library is also using. Changed the public Archive API: - More permissive with the types accepted for archive filename, destination and password. - Return PathBufs rather than Strings when representing filenames. Also tried to avoid taking ownership of filename inside Archive. But despite the efforts, all the input strings/paths still end up getting copied in the conversion to (Wide)CString. Passwords are still Strings, as the old RARSetPassword interface only supports char. It will be necessary to switch to using UCM_NEEDPASSWORDW first. Apparently this is also required for archives with encrypted headers. Fixes #11, and partially addresses #4. --- Cargo.toml | 1 + src/archive.rs | 255 +++++++++++++++++++++++++------------------ src/lib.rs | 1 + unrar_sys/src/lib.rs | 8 +- 4 files changed, 155 insertions(+), 110 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6d9d848..1a2f27b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ lazy_static = "1" enum_primitive = "0.1" num = "0.1" bitflags = "1" +widestring = "0.4" [dependencies.unrar_sys] path = "unrar_sys" diff --git a/src/archive.rs b/src/archive.rs index c8a387a..71cf4cb 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,9 +1,12 @@ use native; +use widestring::WideCString; use regex::Regex; -use std::os::raw::c_int; +use std::os::raw::{c_int, c_uint}; use std::str; use std::fmt; -use std::ffi::{CString, CStr}; +use std::borrow::Cow; +use std::path::{Path, PathBuf}; +use std::ffi::CString; use std::iter::repeat; use std::ptr::NonNull; use error::*; @@ -31,28 +34,35 @@ lazy_static! { } pub struct Archive<'a> { - filename: String, - password: Option, + filename: Cow<'a, Path>, + password: Option<&'a str>, comments: Option<&'a mut Vec>, } -pub type Glob = String; +pub type Glob = PathBuf; impl<'a> Archive<'a> { /// Creates an `Archive` object to operate on a plain RAR archive. - pub fn new(file: String) -> Self { + pub fn new(file: &'a T) -> Self + where + T: AsRef + ?Sized, + { Archive { - filename: file, + filename: Cow::Borrowed(file.as_ref()), password: None, comments: None, } } /// Creates an `Archive` object to operate on a password encrypted RAR archive. - pub fn with_password(file: String, password: String) -> Self { + pub fn with_password(file: &'a T, password: &'a U) -> Self + where + T: AsRef + ?Sized, + U: AsRef + ?Sized, + { Archive { - filename: file, - password: Some(password), + filename: Cow::Borrowed(file.as_ref()), + password: Some(password.as_ref()), comments: None, } } @@ -82,13 +92,17 @@ impl<'a> Archive<'a> { /// /// This method does not make any FS operations and operates purely on strings. pub fn all_parts_option(&self) -> Option { - MULTIPART_EXTENSION.captures(&self.filename).map(|captures| { - let mut replacement = String::from(captures.get(1).unwrap().as_str()); - replacement.push_str(&repeat("?") - .take(captures.get(2).unwrap().as_str().len()) - .collect::()); - replacement.push_str(captures.get(3).unwrap().as_str()); - self.filename.replace(captures.get(0).unwrap().as_str(), &replacement) + get_rar_extension(&self.filename).and_then(|full_ext| { + MULTIPART_EXTENSION.captures(&full_ext).map(|captures| { + let mut replacement = String::from(captures.get(1).unwrap().as_str()); + replacement.push_str(&repeat("?") + .take(captures.get(2).unwrap().as_str().len()) + .collect::()); + replacement.push_str(captures.get(3).unwrap().as_str()); + full_ext.replace(captures.get(0).unwrap().as_str(), &replacement) + }) + }).and_then(|new_ext| { + self.filename.file_stem().map(|x| Path::new(x).with_extension(&new_ext[1..])) }) } @@ -99,7 +113,7 @@ impl<'a> Archive<'a> { pub fn all_parts(&self) -> Glob { match self.all_parts_option() { Some(x) => x, - None => self.filename.clone(), + None => self.filename.to_path_buf(), } } @@ -107,13 +121,17 @@ impl<'a> Archive<'a> { /// if the underlying archive is single part /// /// This method does not make any FS operations and operates purely on strings. - pub fn nth_part(&self, n: i32) -> Option { - MULTIPART_EXTENSION.captures(&self.filename).map(|captures| { - let mut replacement = String::from(captures.get(1).unwrap().as_str()); - // `n` padded with zeroes to the length of archive's number's length - replacement.push_str(&format!("{:01$}", n, captures.get(2).unwrap().as_str().len())); - replacement.push_str(captures.get(3).unwrap().as_str()); - self.filename.replace(captures.get(0).unwrap().as_str(), &replacement) + pub fn nth_part(&self, n: i32) -> Option { + get_rar_extension(&self.filename).and_then(|full_ext| { + MULTIPART_EXTENSION.captures(&full_ext).map(|captures| { + let mut replacement = String::from(captures.get(1).unwrap().as_str()); + // `n` padded with zeroes to the length of archive's number's length + replacement.push_str(&format!("{:01$}", n, captures.get(2).unwrap().as_str().len())); + replacement.push_str(captures.get(3).unwrap().as_str()); + full_ext.replace(captures.get(0).unwrap().as_str(), &replacement) + }) + }).and_then(|new_ext| { + self.filename.file_stem().map(|x| Path::new(x).with_extension(&new_ext[1..])) }) } @@ -121,7 +139,7 @@ impl<'a> Archive<'a> { /// if the underlying archive is single part /// /// This method does not make any FS operations and operates purely on strings. - pub fn first_part_option(&self) -> Option { + pub fn first_part_option(&self) -> Option { self.nth_part(1) } @@ -129,10 +147,10 @@ impl<'a> Archive<'a> { /// a copy of the underlying archive's filename if it's a single-part archive. /// /// This method does not make any FS operations and operates purely on strings. - pub fn first_part(&self) -> String { + pub fn first_part(&self) -> PathBuf { match self.nth_part(1) { Some(x) => x, - None => self.filename.clone(), + None => self.filename.to_path_buf(), } } @@ -141,42 +159,40 @@ impl<'a> Archive<'a> { /// /// This method does not make any FS operations and operates purely on strings. pub fn as_first_part(&mut self) { - self.first_part_option().map(|fp| self.filename = fp); + self.first_part_option().map(|fp| self.filename = Cow::Owned(fp)); } /// Opens the underlying archive for listing its contents pub fn list(self) -> UnrarResult { - self.open(OpenMode::List, None, Operation::Skip) + self.open::<&str>(OpenMode::List, None, Operation::Skip) } /// Opens the underlying archive for listing its contents /// without omitting or pooling split entries pub fn list_split(self) -> UnrarResult { - self.open(OpenMode::ListSplit, None, Operation::Skip) + self.open::<&str>(OpenMode::ListSplit, None, Operation::Skip) } /// Opens the underlying archive for extracting to the given directory. - pub fn extract_to(self, path: String) -> UnrarResult { + pub fn extract_to>(self, path: T) -> UnrarResult { self.open(OpenMode::Extract, Some(path), Operation::Extract) } /// Opens the underlying archive for testing. pub fn test(self) -> UnrarResult { - self.open(OpenMode::Extract, None, Operation::Test) + self.open::<&str>(OpenMode::Extract, None, Operation::Test) } /// Opens the underlying archive with the provided parameters. - pub fn open(self, + pub fn open>(self, mode: OpenMode, - path: Option, + path: Option, operation: Operation) -> UnrarResult { - let path = path.and_then(|x| Some(CString::new(x).unwrap())); - let password = self.password.and_then(|x| Some(CString::new(x).unwrap())); - let filename = CString::new(self.filename) - .map_err(|_| UnrarError::from(Code::Unknown, When::Open))?; + // Panics if password contains nul values. + let password = self.password.map(|x| CString::new(x).unwrap()); - OpenArchive::new(filename, mode, password, path, operation) + OpenArchive::new(&self.filename, mode, password, path.as_ref().map(|x| x.as_ref()), operation) } } @@ -184,20 +200,23 @@ impl<'a> Archive<'a> { pub struct OpenArchive { handle: NonNull, operation: Operation, - destination: Option, + destination: Option, damaged: bool, } impl OpenArchive { - fn new(filename: CString, + fn new(filename: &Path, mode: OpenMode, password: Option, - destination: Option, + destination: Option<&Path>, operation: Operation) - -> UnrarResult { - let mut data = native::OpenArchiveData::new(filename.as_ptr() as *const _, - mode as u32); - let handle = NonNull::new(unsafe { native::RAROpenArchive(&mut data as *mut _) } + -> UnrarResult + { + // Panics if filename contains nul values. + let filename = WideCString::from_os_str(&filename).unwrap(); + let mut data = native::OpenArchiveDataEx::new(filename.as_ptr() as *const _, + mode as u32); + let handle = NonNull::new(unsafe { native::RAROpenArchiveEx(&mut data as *mut _) } as *mut _); let result = Code::from(data.open_result).unwrap(); @@ -208,7 +227,8 @@ impl OpenArchive { let archive = OpenArchive { handle: handle, - destination: destination, + // Panics if destination contains nul values. + destination: destination.map(|p| WideCString::from_os_str(&p).unwrap()), damaged: false, operation: operation, }; @@ -238,11 +258,13 @@ impl OpenArchive { p1: native::LPARAM, p2: native::LPARAM) -> c_int { // println!("msg: {}, user_data: {}, p1: {}, p2: {}", msg, user_data, p1, p2); match msg { - native::UCM_CHANGEVOLUME => { + native::UCM_CHANGEVOLUMEW => { let ptr = p1 as *const _; - let next = unsafe { CStr::from_ptr(ptr) }.to_owned(); - let our_option = unsafe { &mut *(user_data as *mut enum_primitive::Option) }; - *our_option = Some(next); + // 2048 seems to be the buffer size in unrar, + // also it's the maximum path length since 5.00. + let next = unsafe { WideCString::from_ptr_with_nul(ptr, 2048) }.ok(); + let our_option = unsafe { &mut *(user_data as *mut Option) }; + *our_option = next; match p2 { // Next volume not found. -1 means stop native::RAR_VOL_ASK => -1, @@ -263,18 +285,18 @@ impl Iterator for OpenArchive { if self.damaged { return None; } - let mut volume: Option = None; + let mut volume: Option = None; unsafe { native::RARSetCallback(self.handle.as_ptr(), Self::callback, &mut volume as *mut _ as native::LPARAM) } - let mut header = native::HeaderData::default(); + let mut header = native::HeaderDataEx::default(); let read_result = - Code::from(unsafe { native::RARReadHeader(self.handle.as_ptr(), &mut header as *mut _) as u32 }) + Code::from(unsafe { native::RARReadHeaderEx(self.handle.as_ptr(), &mut header as *mut _) as u32 }) .unwrap(); match read_result { Code::Success => { let process_result = Code::from(unsafe { - native::RARProcessFile( + native::RARProcessFileW( self.handle.as_ptr(), self.operation as i32, self.destination.as_ref().map( @@ -282,15 +304,14 @@ impl Iterator for OpenArchive { ).unwrap_or(0 as *const _), 0 as *const _ ) as u32 - }) - .unwrap(); + }).unwrap(); + match process_result { Code::Success | Code::EOpen => { let mut entry = Entry::from(header); // EOpen on Process: Next volume not found if process_result == Code::EOpen { - entry.next_volume = volume.as_ref() - .and_then(|x| Some(x.to_str().unwrap().to_owned())); + entry.next_volume = volume.map(|x| PathBuf::from(x.to_os_string())); self.damaged = true; Some(Err(UnrarError::new(process_result, When::Process, entry))) } else { @@ -320,6 +341,10 @@ impl Drop for OpenArchive { } } +fn unpack_unp_size(unp_size: c_uint, unp_size_high: c_uint) -> usize { + (unp_size_high << std::mem::size_of::() | unp_size) as usize +} + bitflags! { pub struct EntryFlags: u32 { const SPLIT_BEFORE = 0x1; @@ -333,14 +358,14 @@ bitflags! { #[derive(Debug)] pub struct Entry { - pub filename: String, + pub filename: PathBuf, pub flags: EntryFlags, - pub unpacked_size: u32, + pub unpacked_size: usize, pub file_crc: u32, pub file_time: u32, pub method: u32, pub file_attr: u32, - pub next_volume: Option, + pub next_volume: Option, } impl Entry { @@ -363,7 +388,7 @@ impl Entry { impl fmt::Display for Entry { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.filename)?; + write!(f, "{:?}", self.filename)?; if self.is_directory() { write!(f, "/")? } @@ -374,13 +399,15 @@ impl fmt::Display for Entry { } } -impl From for Entry { - fn from(header: native::HeaderData) -> Self { +impl From for Entry { + fn from(header: native::HeaderDataEx) -> Self { + let filename = unsafe { WideCString::from_ptr_with_nul(header.filename_w.as_ptr() + as *const _, 1024) }.unwrap(); + Entry { - filename: unsafe { CStr::from_ptr(header.filename.as_ptr()) }.to_str().unwrap() - .to_owned(), + filename: PathBuf::from(filename.to_os_string()), flags: EntryFlags::from_bits(header.flags).unwrap(), - unpacked_size: header.unp_size, + unpacked_size: unpack_unp_size(header.unp_size, header.unp_size_high), file_crc: header.file_crc, file_time: header.file_time, method: header.method, @@ -390,59 +417,75 @@ impl From for Entry { } } -pub fn is_archive(s: &str) -> bool { - EXTENSION.find(s).is_some() +fn get_rar_extension>(path: T) -> Option { + path.as_ref().extension().map(|ext| { + let pre_ext = path.as_ref().file_stem().and_then(|x| Path::new(x).extension()); + match pre_ext { + Some(pre_ext) => format!(".{}.{}", pre_ext.to_string_lossy(), ext.to_string_lossy()), + None => format!(".{}", ext.to_string_lossy()) + } + }) +} + +pub fn is_archive(s: &Path) -> bool { + get_rar_extension(s).and_then(|full_ext| { + EXTENSION.find(&full_ext).map(|_| ()) + }).is_some() } -pub fn is_multipart(s: &str) -> bool { - MULTIPART_EXTENSION.find(s).is_some() +pub fn is_multipart(s: &Path) -> bool { + get_rar_extension(s).and_then(|full_ext| { + MULTIPART_EXTENSION.find(&full_ext).map(|_| ()) + }).is_some() } #[cfg(test)] mod tests { use super::Archive; + use std::path::PathBuf; + #[test] fn glob() { - assert_eq!(Archive::new("arc.part0010.rar".into()).all_parts(), - "arc.part????.rar"); - assert_eq!(Archive::new("archive.r100".into()).all_parts(), - "archive.r???"); - assert_eq!(Archive::new("archive.r9".into()).all_parts(), "archive.r?"); - assert_eq!(Archive::new("archive.999".into()).all_parts(), - "archive.???"); - assert_eq!(Archive::new("archive.rar".into()).all_parts(), - "archive.rar"); - assert_eq!(Archive::new("random_string".into()).all_parts(), - "random_string"); - assert_eq!(Archive::new("v8/v8.rar".into()).all_parts(), "v8/v8.rar"); - assert_eq!(Archive::new("v8/v8".into()).all_parts(), "v8/v8"); + assert_eq!(Archive::new("arc.part0010.rar").all_parts(), + PathBuf::from("arc.part????.rar")); + assert_eq!(Archive::new("archive.r100").all_parts(), + PathBuf::from("archive.r???")); + assert_eq!(Archive::new("archive.r9").all_parts(), PathBuf::from("archive.r?")); + assert_eq!(Archive::new("archive.999").all_parts(), + PathBuf::from("archive.???")); + assert_eq!(Archive::new("archive.rar").all_parts(), + PathBuf::from("archive.rar")); + assert_eq!(Archive::new("random_string").all_parts(), + PathBuf::from("random_string")); + assert_eq!(Archive::new("v8/v8.rar").all_parts(), PathBuf::from("v8/v8.rar")); + assert_eq!(Archive::new("v8/v8").all_parts(), PathBuf::from("v8/v8")); } #[test] fn first_part() { - assert_eq!(Archive::new("arc.part0010.rar".into()).first_part(), - "arc.part0001.rar"); - assert_eq!(Archive::new("archive.r100".into()).first_part(), - "archive.r001"); - assert_eq!(Archive::new("archive.r9".into()).first_part(), "archive.r1"); - assert_eq!(Archive::new("archive.999".into()).first_part(), - "archive.001"); - assert_eq!(Archive::new("archive.rar".into()).first_part(), - "archive.rar"); - assert_eq!(Archive::new("random_string".into()).first_part(), - "random_string"); - assert_eq!(Archive::new("v8/v8.rar".into()).first_part(), "v8/v8.rar"); - assert_eq!(Archive::new("v8/v8".into()).first_part(), "v8/v8"); + assert_eq!(Archive::new("arc.part0010.rar").first_part(), + PathBuf::from("arc.part0001.rar")); + assert_eq!(Archive::new("archive.r100").first_part(), + PathBuf::from("archive.r001")); + assert_eq!(Archive::new("archive.r9").first_part(), PathBuf::from("archive.r1")); + assert_eq!(Archive::new("archive.999").first_part(), + PathBuf::from("archive.001")); + assert_eq!(Archive::new("archive.rar").first_part(), + PathBuf::from("archive.rar")); + assert_eq!(Archive::new("random_string").first_part(), + PathBuf::from("random_string")); + assert_eq!(Archive::new("v8/v8.rar").first_part(), PathBuf::from("v8/v8.rar")); + assert_eq!(Archive::new("v8/v8").first_part(), PathBuf::from("v8/v8")); } #[test] fn is_archive() { - assert_eq!(super::is_archive("archive.rar"), true); - assert_eq!(super::is_archive("archive.part1.rar"), true); - assert_eq!(super::is_archive("archive.part100.rar"), true); - assert_eq!(super::is_archive("archive.r10"), true); - assert_eq!(super::is_archive("archive.part1rar"), false); - assert_eq!(super::is_archive("archive.rar\n"), false); - assert_eq!(super::is_archive("archive.zip"), false); + assert_eq!(super::is_archive(&PathBuf::from("archive.rar")), true); + assert_eq!(super::is_archive(&PathBuf::from("archive.part1.rar")), true); + assert_eq!(super::is_archive(&PathBuf::from("archive.part100.rar")), true); + assert_eq!(super::is_archive(&PathBuf::from("archive.r10")), true); + assert_eq!(super::is_archive(&PathBuf::from("archive.part1rar")), false); + assert_eq!(super::is_archive(&PathBuf::from("archive.rar\n")), false); + assert_eq!(super::is_archive(&PathBuf::from("archive.zip")), false); } } diff --git a/src/lib.rs b/src/lib.rs index 12b6ca5..22d2c63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ extern crate lazy_static; extern crate enum_primitive; #[macro_use] extern crate bitflags; +extern crate widestring; pub use archive::Archive; pub mod error; diff --git a/unrar_sys/src/lib.rs b/unrar_sys/src/lib.rs index d44897c..a7202a3 100644 --- a/unrar_sys/src/lib.rs +++ b/unrar_sys/src/lib.rs @@ -327,12 +327,12 @@ impl OpenArchiveData { } } -impl Default for OpenArchiveDataEx { - fn default() -> Self { +impl OpenArchiveDataEx { + pub fn new(archive: *const wchar_t, mode: c_uint) -> Self { OpenArchiveDataEx { archive_name: 0 as *const _, - archive_name_w: 0 as *const _, - open_mode: 0, + archive_name_w: archive, + open_mode: mode, open_result: 0, comment_buffer: 0 as *mut _, comment_buffer_size: 0, From 79822f9737818ebb63d4b304df0bbb320046f33d Mon Sep 17 00:00:00 2001 From: vjoki Date: Mon, 25 Nov 2019 00:54:59 +0200 Subject: [PATCH 5/7] Update tests and examples --- examples/basic_extract.rs | 2 +- examples/basic_list.rs | 2 +- examples/lister.rs | 4 ++-- tests/crypted.rs | 13 +++++++------ tests/multipart.rs | 8 +++----- tests/simple.rs | 9 +++++---- tests/utf8.rs | 5 +++-- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/examples/basic_extract.rs b/examples/basic_extract.rs index ffcc764..edd4525 100644 --- a/examples/basic_extract.rs +++ b/examples/basic_extract.rs @@ -3,6 +3,6 @@ extern crate unrar; use unrar::Archive; fn main() { - Archive::new("archive.rar".into()).extract_to("./archive".into()).unwrap().process().unwrap(); + Archive::new("archive.rar").extract_to("./archive").unwrap().process().unwrap(); println!("Done."); } diff --git a/examples/basic_list.rs b/examples/basic_list.rs index a96dcee..aa28f32 100644 --- a/examples/basic_list.rs +++ b/examples/basic_list.rs @@ -3,7 +3,7 @@ extern crate unrar; use unrar::Archive; fn main() { - for entry in Archive::new("archive.rar".into()).list().unwrap() { + for entry in Archive::new("archive.rar").list().unwrap() { println!("{}", entry.unwrap()); } } diff --git a/examples/lister.rs b/examples/lister.rs index 5f0f1e7..61ca861 100644 --- a/examples/lister.rs +++ b/examples/lister.rs @@ -15,7 +15,7 @@ fn main() { std::process::exit(0) }); - match Archive::new(file).list_split() { + match Archive::new(&file).list_split() { // Everything okay, just list the archive Ok(archive) => list_archive(archive), @@ -46,7 +46,7 @@ fn main() { println!("{}", e); // emit warning that an error occured. writeln!(&mut stderr, - "Could not find volume: {}", + "Could not find volume: {:?}", e.next_volume.unwrap()) .unwrap(); // The iterator will stop by itself, no further action needed. diff --git a/tests/crypted.rs b/tests/crypted.rs index 7badb87..b781cdf 100644 --- a/tests/crypted.rs +++ b/tests/crypted.rs @@ -6,19 +6,20 @@ use unrar::error::{Code, When}; use tempdir::TempDir; use std::fs::File; use std::io::prelude::*; +use std::path::PathBuf; #[test] fn list() { // No password needed in order to list contents - let mut entries = Archive::new("data/crypted.rar".into()).list().unwrap(); - assert_eq!(entries.next().unwrap().unwrap().filename, ".gitignore"); + let mut entries = Archive::new("data/crypted.rar").list().unwrap(); + assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from(".gitignore")); } #[test] fn no_password() { let t = TempDir::new("unrar").unwrap(); - let mut arc = Archive::new("data/crypted.rar".into()) - .extract_to(t.path().to_str().unwrap().into()) + let mut arc = Archive::new("data/crypted.rar") + .extract_to(t.path()) .unwrap(); let err = arc.next().unwrap().unwrap_err(); assert_eq!(err.code, Code::MissingPassword); @@ -28,8 +29,8 @@ fn no_password() { #[test] fn version_cat() { let t = TempDir::new("unrar").unwrap(); - Archive::with_password("data/crypted.rar".into(), "unrar".into()) - .extract_to(t.path().to_str().unwrap().into()) + Archive::with_password("data/crypted.rar", "unrar") + .extract_to(t.path()) .unwrap() .process() .unwrap(); diff --git a/tests/multipart.rs b/tests/multipart.rs index e7aa33b..ff365e8 100644 --- a/tests/multipart.rs +++ b/tests/multipart.rs @@ -1,10 +1,8 @@ -extern crate tempdir; extern crate unrar; use unrar::Archive; use unrar::error::{Code, When}; use std::path::PathBuf; -use std::io::prelude::*; #[test] fn list_missing_volume() { @@ -14,14 +12,14 @@ fn list_missing_volume() { "src/lib.rs", "vendor/unrar/acknow.txt", "vendor/unrar/arccmt.cpp"].iter().map(|x| x.into()).collect(); - let mut archive = Archive::new("data/archive.part1.rar".into()).list().unwrap(); + let mut archive = Archive::new("data/archive.part1.rar").list().unwrap(); for (i, e) in archive.by_ref().enumerate().take(expected.len()) { - assert_eq!(PathBuf::from(e.unwrap().filename), expected[i]); + assert_eq!(e.unwrap().filename, expected[i]); } let err = archive.next().unwrap().err().unwrap(); assert_eq!(err.code, Code::EOpen); assert_eq!(err.when, When::Process); let data = err.data.unwrap(); - assert_eq!(PathBuf::from(data.filename), PathBuf::from("vendor/unrar/archive.cpp")); + assert_eq!(data.filename, PathBuf::from("vendor/unrar/archive.cpp")); assert_eq!(PathBuf::from(data.next_volume.unwrap()), PathBuf::from("data/archive.part2.rar")); } diff --git a/tests/simple.rs b/tests/simple.rs index 133f193..46318be 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -4,18 +4,19 @@ extern crate unrar; use tempdir::TempDir; use std::fs::File; use std::io::prelude::*; +use std::path::PathBuf; #[test] fn version_list() { - let mut entries = unrar::Archive::new("data/version.rar".into()).list().unwrap(); - assert_eq!(entries.next().unwrap().unwrap().filename, "VERSION"); + let mut entries = unrar::Archive::new("data/version.rar").list().unwrap(); + assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from("VERSION")); } #[test] fn version_cat() { let t = TempDir::new("unrar").unwrap(); - unrar::Archive::new("data/version.rar".into()) - .extract_to(t.path().to_str().unwrap().into()) + unrar::Archive::new("data/version.rar") + .extract_to(t.path()) .unwrap() .process() .unwrap(); diff --git a/tests/utf8.rs b/tests/utf8.rs index 72b8012..74a01bb 100644 --- a/tests/utf8.rs +++ b/tests/utf8.rs @@ -1,9 +1,10 @@ extern crate unrar; use unrar::Archive; +use std::path::PathBuf; #[test] fn unicode_list() { - let mut entries = Archive::new("data/unicode.rar".into()).list().unwrap(); - assert_eq!(entries.next().unwrap().unwrap().filename, "te…―st✌"); + let mut entries = Archive::new("data/unicode.rar").list().unwrap(); + assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from("te…―st✌")); } From a856bacc706132f9a19830bdad5e6136798028a1 Mon Sep 17 00:00:00 2001 From: vjoki Date: Fri, 27 Dec 2019 12:34:42 +0200 Subject: [PATCH 6/7] Don't panic, error out on nul values --- src/archive.rs | 39 ++++++++++++++++++++++++++++++--------- src/error.rs | 12 ++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 71cf4cb..9d406c3 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -189,10 +189,7 @@ impl<'a> Archive<'a> { path: Option, operation: Operation) -> UnrarResult { - // Panics if password contains nul values. - let password = self.password.map(|x| CString::new(x).unwrap()); - - OpenArchive::new(&self.filename, mode, password, path.as_ref().map(|x| x.as_ref()), operation) + OpenArchive::new(&self.filename, mode, self.password, path.as_ref().map(|x| x.as_ref()), operation) } } @@ -207,13 +204,21 @@ pub struct OpenArchive { impl OpenArchive { fn new(filename: &Path, mode: OpenMode, - password: Option, + password: Option<&str>, destination: Option<&Path>, operation: Operation) -> UnrarResult { - // Panics if filename contains nul values. - let filename = WideCString::from_os_str(&filename).unwrap(); + let password = match password { + Some(pw) => Some(CString::new(pw)?), + None => None, + }; + let destination = match destination { + Some(dest) => Some(WideCString::from_os_str(&dest)?), + None => None, + }; + let filename = WideCString::from_os_str(&filename)?; + let mut data = native::OpenArchiveDataEx::new(filename.as_ptr() as *const _, mode as u32); let handle = NonNull::new(unsafe { native::RAROpenArchiveEx(&mut data as *mut _) } @@ -227,8 +232,7 @@ impl OpenArchive { let archive = OpenArchive { handle: handle, - // Panics if destination contains nul values. - destination: destination.map(|p| WideCString::from_os_str(&p).unwrap()), + destination: destination, damaged: false, operation: operation, }; @@ -488,4 +492,21 @@ mod tests { assert_eq!(super::is_archive(&PathBuf::from("archive.rar\n")), false); assert_eq!(super::is_archive(&PathBuf::from("archive.zip")), false); } + + #[test] + fn nul_in_input() { + use crate::error::{Code, When}; + + let err = Archive::new("\0archive.rar").list().unwrap_err(); + assert_eq!(err.code, Code::Unknown); + assert_eq!(err.when, When::Open); + + let err = Archive::with_password("archive.rar", "un\0rar").list().unwrap_err(); + assert_eq!(err.code, Code::Unknown); + assert_eq!(err.when, When::Open); + + let err = Archive::new("archive.rar").extract_to("tmp/\0").unwrap_err(); + assert_eq!(err.code, Code::Unknown); + assert_eq!(err.when, When::Open); + } } diff --git a/src/error.rs b/src/error.rs index ce33424..ee527c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -107,3 +107,15 @@ impl UnrarError { } pub type UnrarResult = Result>; + +impl From> for UnrarError { + fn from(_: widestring::NulError) -> UnrarError { + UnrarError::from(Code::Unknown, When::Open) + } +} + +impl From for UnrarError { + fn from(_: std::ffi::NulError) -> UnrarError { + UnrarError::from(Code::Unknown, When::Open) + } +} From 2d6b88289b3367e1a3ed843f1d9afd08f5869ce4 Mon Sep 17 00:00:00 2001 From: vjoki Date: Fri, 17 Jan 2020 17:11:46 +0200 Subject: [PATCH 7/7] Try to fail earlier on nul values - Add NulError, so that we can combine ffi::NulError and widestring::NulError. - Check filename and password for nul values on Archive creation. --- examples/basic_extract.rs | 2 +- examples/basic_list.rs | 2 +- examples/lister.rs | 2 +- src/archive.rs | 89 ++++++++++++++++++++------------------- src/error.rs | 29 ++++++++++--- tests/crypted.rs | 6 +-- tests/multipart.rs | 2 +- tests/simple.rs | 4 +- tests/utf8.rs | 2 +- 9 files changed, 79 insertions(+), 59 deletions(-) diff --git a/examples/basic_extract.rs b/examples/basic_extract.rs index edd4525..1a9ee1e 100644 --- a/examples/basic_extract.rs +++ b/examples/basic_extract.rs @@ -3,6 +3,6 @@ extern crate unrar; use unrar::Archive; fn main() { - Archive::new("archive.rar").extract_to("./archive").unwrap().process().unwrap(); + Archive::new("archive.rar").unwrap().extract_to("./archive").unwrap().process().unwrap(); println!("Done."); } diff --git a/examples/basic_list.rs b/examples/basic_list.rs index aa28f32..5b8f99f 100644 --- a/examples/basic_list.rs +++ b/examples/basic_list.rs @@ -3,7 +3,7 @@ extern crate unrar; use unrar::Archive; fn main() { - for entry in Archive::new("archive.rar").list().unwrap() { + for entry in Archive::new("archive.rar").unwrap().list().unwrap() { println!("{}", entry.unwrap()); } } diff --git a/examples/lister.rs b/examples/lister.rs index 61ca861..f27dd08 100644 --- a/examples/lister.rs +++ b/examples/lister.rs @@ -15,7 +15,7 @@ fn main() { std::process::exit(0) }); - match Archive::new(&file).list_split() { + match Archive::new(&file).unwrap().list_split() { // Everything okay, just list the archive Ok(archive) => list_archive(archive), diff --git a/src/archive.rs b/src/archive.rs index 9d406c3..ebfc9a8 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -35,7 +35,7 @@ lazy_static! { pub struct Archive<'a> { filename: Cow<'a, Path>, - password: Option<&'a str>, + password: Option, comments: Option<&'a mut Vec>, } @@ -43,28 +43,30 @@ pub type Glob = PathBuf; impl<'a> Archive<'a> { /// Creates an `Archive` object to operate on a plain RAR archive. - pub fn new(file: &'a T) -> Self + pub fn new(file: &'a T) -> Result where T: AsRef + ?Sized, { - Archive { + let _ = WideCString::from_os_str(file.as_ref())?; + Ok(Archive { filename: Cow::Borrowed(file.as_ref()), password: None, comments: None, - } + }) } /// Creates an `Archive` object to operate on a password encrypted RAR archive. - pub fn with_password(file: &'a T, password: &'a U) -> Self + pub fn with_password(file: &'a T, password: &'a U) -> Result where T: AsRef + ?Sized, U: AsRef + ?Sized, { - Archive { + let _ = WideCString::from_os_str(file.as_ref())?; + Ok(Archive { filename: Cow::Borrowed(file.as_ref()), - password: Some(password.as_ref()), + password: Some(CString::new(password.as_ref())?), comments: None, - } + }) } /// Set the comment buffer of the underlying archive. @@ -174,6 +176,10 @@ impl<'a> Archive<'a> { } /// Opens the underlying archive for extracting to the given directory. + /// + /// # Panics + /// + /// Panics if `path` contains nul values. pub fn extract_to>(self, path: T) -> UnrarResult { self.open(OpenMode::Extract, Some(path), Operation::Extract) } @@ -184,6 +190,10 @@ impl<'a> Archive<'a> { } /// Opens the underlying archive with the provided parameters. + /// + /// # Panics + /// + /// Panics if `path` contains nul values. pub fn open>(self, mode: OpenMode, path: Option, @@ -204,20 +214,18 @@ pub struct OpenArchive { impl OpenArchive { fn new(filename: &Path, mode: OpenMode, - password: Option<&str>, + password: Option, destination: Option<&Path>, operation: Operation) -> UnrarResult { - let password = match password { - Some(pw) => Some(CString::new(pw)?), - None => None, - }; let destination = match destination { - Some(dest) => Some(WideCString::from_os_str(&dest)?), + Some(dest) => Some(WideCString::from_os_str(&dest).expect("Unexpected nul in destination")), None => None, }; - let filename = WideCString::from_os_str(&filename)?; + // Panic here is our fault. Either something in Archive has added a nul to filename, + // or filename was not checked for nuls on Archive creation. + let filename = WideCString::from_os_str(&filename).expect("Unexpected nul in filename"); let mut data = native::OpenArchiveDataEx::new(filename.as_ptr() as *const _, mode as u32); @@ -450,36 +458,36 @@ mod tests { #[test] fn glob() { - assert_eq!(Archive::new("arc.part0010.rar").all_parts(), + assert_eq!(Archive::new("arc.part0010.rar").unwrap().all_parts(), PathBuf::from("arc.part????.rar")); - assert_eq!(Archive::new("archive.r100").all_parts(), + assert_eq!(Archive::new("archive.r100").unwrap().all_parts(), PathBuf::from("archive.r???")); - assert_eq!(Archive::new("archive.r9").all_parts(), PathBuf::from("archive.r?")); - assert_eq!(Archive::new("archive.999").all_parts(), + assert_eq!(Archive::new("archive.r9").unwrap().all_parts(), PathBuf::from("archive.r?")); + assert_eq!(Archive::new("archive.999").unwrap().all_parts(), PathBuf::from("archive.???")); - assert_eq!(Archive::new("archive.rar").all_parts(), + assert_eq!(Archive::new("archive.rar").unwrap().all_parts(), PathBuf::from("archive.rar")); - assert_eq!(Archive::new("random_string").all_parts(), + assert_eq!(Archive::new("random_string").unwrap().all_parts(), PathBuf::from("random_string")); - assert_eq!(Archive::new("v8/v8.rar").all_parts(), PathBuf::from("v8/v8.rar")); - assert_eq!(Archive::new("v8/v8").all_parts(), PathBuf::from("v8/v8")); + assert_eq!(Archive::new("v8/v8.rar").unwrap().all_parts(), PathBuf::from("v8/v8.rar")); + assert_eq!(Archive::new("v8/v8").unwrap().all_parts(), PathBuf::from("v8/v8")); } #[test] fn first_part() { - assert_eq!(Archive::new("arc.part0010.rar").first_part(), + assert_eq!(Archive::new("arc.part0010.rar").unwrap().first_part(), PathBuf::from("arc.part0001.rar")); - assert_eq!(Archive::new("archive.r100").first_part(), + assert_eq!(Archive::new("archive.r100").unwrap().first_part(), PathBuf::from("archive.r001")); - assert_eq!(Archive::new("archive.r9").first_part(), PathBuf::from("archive.r1")); - assert_eq!(Archive::new("archive.999").first_part(), + assert_eq!(Archive::new("archive.r9").unwrap().first_part(), PathBuf::from("archive.r1")); + assert_eq!(Archive::new("archive.999").unwrap().first_part(), PathBuf::from("archive.001")); - assert_eq!(Archive::new("archive.rar").first_part(), + assert_eq!(Archive::new("archive.rar").unwrap().first_part(), PathBuf::from("archive.rar")); - assert_eq!(Archive::new("random_string").first_part(), + assert_eq!(Archive::new("random_string").unwrap().first_part(), PathBuf::from("random_string")); - assert_eq!(Archive::new("v8/v8.rar").first_part(), PathBuf::from("v8/v8.rar")); - assert_eq!(Archive::new("v8/v8").first_part(), PathBuf::from("v8/v8")); + assert_eq!(Archive::new("v8/v8.rar").unwrap().first_part(), PathBuf::from("v8/v8.rar")); + assert_eq!(Archive::new("v8/v8").unwrap().first_part(), PathBuf::from("v8/v8")); } #[test] @@ -495,18 +503,13 @@ mod tests { #[test] fn nul_in_input() { - use crate::error::{Code, When}; - - let err = Archive::new("\0archive.rar").list().unwrap_err(); - assert_eq!(err.code, Code::Unknown); - assert_eq!(err.when, When::Open); - - let err = Archive::with_password("archive.rar", "un\0rar").list().unwrap_err(); - assert_eq!(err.code, Code::Unknown); - assert_eq!(err.when, When::Open); + assert!(Archive::new("\0archive.rar").is_err()); + assert!(Archive::with_password("archive.rar", "un\0rar").is_err()); + } - let err = Archive::new("archive.rar").extract_to("tmp/\0").unwrap_err(); - assert_eq!(err.code, Code::Unknown); - assert_eq!(err.when, When::Open); + #[test] + #[should_panic(expected = "Unexpected nul in destination")] + fn nul_in_destination() { + let _ = Archive::new("archive.rar").unwrap().extract_to("tmp/\0"); } } diff --git a/src/error.rs b/src/error.rs index ee527c1..aa9c639 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,8 @@ use native; use std::result::Result; use num::FromPrimitive; use std::fmt; +use std::error; +use std::ffi; enum_from_primitive! { #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -108,14 +110,29 @@ impl UnrarError { pub type UnrarResult = Result>; -impl From> for UnrarError { - fn from(_: widestring::NulError) -> UnrarError { - UnrarError::from(Code::Unknown, When::Open) +#[derive(Debug)] +pub struct NulError(usize); + +impl fmt::Display for NulError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "nul value found at position: {}", self.0) + } +} + +impl error::Error for NulError { + fn description(&self) -> &str { + "nul value found" + } +} + +impl From> for NulError { + fn from(e: widestring::NulError) -> NulError { + NulError(e.nul_position()) } } -impl From for UnrarError { - fn from(_: std::ffi::NulError) -> UnrarError { - UnrarError::from(Code::Unknown, When::Open) +impl From for NulError { + fn from(e: ffi::NulError) -> NulError { + NulError(e.nul_position()) } } diff --git a/tests/crypted.rs b/tests/crypted.rs index b781cdf..844c6bc 100644 --- a/tests/crypted.rs +++ b/tests/crypted.rs @@ -11,14 +11,14 @@ use std::path::PathBuf; #[test] fn list() { // No password needed in order to list contents - let mut entries = Archive::new("data/crypted.rar").list().unwrap(); + let mut entries = Archive::new("data/crypted.rar").unwrap().list().unwrap(); assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from(".gitignore")); } #[test] fn no_password() { let t = TempDir::new("unrar").unwrap(); - let mut arc = Archive::new("data/crypted.rar") + let mut arc = Archive::new("data/crypted.rar").unwrap() .extract_to(t.path()) .unwrap(); let err = arc.next().unwrap().unwrap_err(); @@ -29,7 +29,7 @@ fn no_password() { #[test] fn version_cat() { let t = TempDir::new("unrar").unwrap(); - Archive::with_password("data/crypted.rar", "unrar") + Archive::with_password("data/crypted.rar", "unrar").unwrap() .extract_to(t.path()) .unwrap() .process() diff --git a/tests/multipart.rs b/tests/multipart.rs index ff365e8..9f64c86 100644 --- a/tests/multipart.rs +++ b/tests/multipart.rs @@ -12,7 +12,7 @@ fn list_missing_volume() { "src/lib.rs", "vendor/unrar/acknow.txt", "vendor/unrar/arccmt.cpp"].iter().map(|x| x.into()).collect(); - let mut archive = Archive::new("data/archive.part1.rar").list().unwrap(); + let mut archive = Archive::new("data/archive.part1.rar").unwrap().list().unwrap(); for (i, e) in archive.by_ref().enumerate().take(expected.len()) { assert_eq!(e.unwrap().filename, expected[i]); } diff --git a/tests/simple.rs b/tests/simple.rs index 46318be..a90c925 100644 --- a/tests/simple.rs +++ b/tests/simple.rs @@ -8,14 +8,14 @@ use std::path::PathBuf; #[test] fn version_list() { - let mut entries = unrar::Archive::new("data/version.rar").list().unwrap(); + let mut entries = unrar::Archive::new("data/version.rar").unwrap().list().unwrap(); assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from("VERSION")); } #[test] fn version_cat() { let t = TempDir::new("unrar").unwrap(); - unrar::Archive::new("data/version.rar") + unrar::Archive::new("data/version.rar").unwrap() .extract_to(t.path()) .unwrap() .process() diff --git a/tests/utf8.rs b/tests/utf8.rs index 74a01bb..c09e36e 100644 --- a/tests/utf8.rs +++ b/tests/utf8.rs @@ -5,6 +5,6 @@ use std::path::PathBuf; #[test] fn unicode_list() { - let mut entries = Archive::new("data/unicode.rar").list().unwrap(); + let mut entries = Archive::new("data/unicode.rar").unwrap().list().unwrap(); assert_eq!(entries.next().unwrap().unwrap().filename, PathBuf::from("te…―st✌")); }