11//! Contains RAR-specific building and unpacking functions
22
3- use std:: path:: Path ;
3+ use std:: path:: { Path , PathBuf } ;
44
5- use unrar:: Archive ;
5+ use unrar:: {
6+ Archive , ExtractEvent ,
7+ error:: { Code , UnrarError , When } ,
8+ } ;
69
710use crate :: {
8- error:: { Error , Result } ,
11+ error:: { Error , FinalError , Result } ,
912 info,
1013 list:: { FileInArchive , ListFileType } ,
11- utils:: BytesFmt ,
14+ utils:: { BytesFmt , PathFmt } ,
1215} ;
1316
1417/// Unpacks the archive given by `archive_path` into the folder given by `output_folder`.
@@ -19,24 +22,46 @@ pub fn unpack_archive(archive_path: &Path, output_folder: &Path, password: Optio
1922 None => Archive :: new ( archive_path) ,
2023 } ;
2124
22- let mut archive = archive. open_for_processing ( ) ?;
23- let mut files_unpacked = 0 ;
25+ let archive = archive. open_for_processing ( ) ?;
26+
27+ let mut files_unpacked: u64 = 0 ;
28+ let mut first_err: Option < ( PathBuf , i32 ) > = None ;
2429
25- while let Some ( header) = archive. read_header ( ) ? {
26- let entry = header. entry ( ) ;
27- archive = if entry. is_file ( ) {
30+ let cb_result = archive. extract_all_with_callback ( output_folder, |event| match event {
31+ ExtractEvent :: Ok { filename, size } => {
32+ info ! ( "extracted ({}) {}" , BytesFmt ( size) , PathFmt ( & filename) ) ;
33+ files_unpacked += 1 ;
34+ true
35+ }
36+ ExtractEvent :: Err { filename, error_code } => {
37+ first_err = Some ( ( filename, error_code) ) ;
38+ // Returning false cancels the rest of the extraction so any
39+ // additional per-file errors don't get silently swallowed.
40+ false
41+ }
42+ ExtractEvent :: LargeDictWarning {
43+ dict_size_kb,
44+ max_dict_size_kb,
45+ } => {
2846 info ! (
29- "extracted ({}) {}" ,
30- BytesFmt ( entry. unpacked_size) ,
31- entry. filename. display( ) ,
47+ "archive requires {} KiB dictionary; this build supports up to {} KiB" ,
48+ dict_size_kb, max_dict_size_kb,
3249 ) ;
33- files_unpacked += 1 ;
34- header. extract_with_base ( output_folder) ?
35- } else {
36- header. skip ( ) ?
37- } ;
38- }
50+ // Reject the oversized dictionary so the DLL fails the
51+ // operation with Code::LargeDict instead of silently
52+ // proceeding with a result it cannot actually produce.
53+ false
54+ }
55+ _ => true ,
56+ } ) ;
3957
58+ if let Some ( ( path, code) ) = first_err {
59+ let inner = UnrarError :: from ( Code :: from ( code) , When :: Process ) . to_string ( ) ;
60+ return Err ( Error :: Custom {
61+ reason : FinalError :: with_title ( format ! ( "failed to extract {}" , PathFmt ( & path) ) ) . detail ( inner) ,
62+ } ) ;
63+ }
64+ let _status = cb_result?;
4065 Ok ( files_unpacked)
4166}
4267
0 commit comments