vendor(unrar): add unrar 7.20.1 (RARLab source, opello mirror)

New RAR engine for v1.9, replacing dmc_unrar 1.7.0. dmc_unrar only
dispatches RAR5 compression version 5 (switch(file->version) case
0x5000); WinRAR 6.x/7.x writes version 6 -> DMC_UNRAR_FILE_UNSUPPORTED_VERSION
-> user-facing "corrupt archive" on real v6 archives (diagnosed 2026-09-05
from a v6:8M:m0:m3 test file). unrar 7.20.1 natively supports v6,
encrypted and multi-volume RAR.

Vendored into third_party/unrar7/ (new dir) so the tree stays buildable
while src/rar_extract.c still references the dmc facade. Contents are a
verbatim copy of opello/unrar @97e1780 (159 files, v7.20.1) plus two
project files:
  - unrar_c_api.h  (C facade shim: platform types + dll.hpp re-export)
  - VENDORED.md    (source pin, build model, license notes)

Build model verified on host (MinGW g++ 16.2, Windows):
  - all 49 UnRARDll.vcxproj sources compile with
    -std=c++17 -DRARDLL -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
  - links + runs: RARGetDllVersion() = 9 (smoke binary in .build/, ignored)
  - Windows link additionally needs -lpowrprof

License: UnRAR freeware license (not GPL) - free to use for handling RAR
archives; cannot be used to build a RAR-compatible archiver. THIRD_PARTY_NOTICES
update lands with the engine swap commit.

Next (v1.9 work items): engine swap in rar_extract.c, Makefile .cpp rules,
fixtures + host tests, frontend password/multi-volume, then ELF build.
This commit is contained in:
Songlx516 committed 2026-09-05 21:57:15 +08:00
1 parent 8344b9bae0
commit c68c0def35
161 files changed
+34224

No files matched your search

+281
View File
@@ -0,0 +1,281 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{95CC809B-03FC-4EDB-BB20-FD07A698C05F}</ProjectGuid>
<RootNamespace>UnRAR</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>build\unrar32\$(Configuration)\</OutDir>
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
<LinkIncremental>true</LinkIncremental>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>build\unrar64\$(Configuration)\</OutDir>
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
<LinkIncremental>true</LinkIncremental>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>build\unrar32\$(Configuration)\</OutDir>
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>build\unrar64\$(Configuration)\</OutDir>
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>StdCall</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>StdCall</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>StdCall</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MinSpace</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>StdCall</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="archive.cpp" />
<ClCompile Include="arcread.cpp" />
<ClCompile Include="blake2s.cpp" />
<ClCompile Include="cmddata.cpp" />
<ClCompile Include="consio.cpp" />
<ClCompile Include="crc.cpp" />
<ClCompile Include="crypt.cpp" />
<ClCompile Include="encname.cpp" />
<ClCompile Include="errhnd.cpp" />
<ClCompile Include="extinfo.cpp" />
<ClCompile Include="extract.cpp" />
<ClCompile Include="filcreat.cpp" />
<ClCompile Include="file.cpp" />
<ClCompile Include="filefn.cpp" />
<ClCompile Include="filestr.cpp" />
<ClCompile Include="find.cpp" />
<ClCompile Include="getbits.cpp" />
<ClCompile Include="global.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="hash.cpp" />
<ClCompile Include="headers.cpp" />
<ClCompile Include="isnt.cpp" />
<ClCompile Include="largepage.cpp" />
<ClCompile Include="list.cpp" />
<ClCompile Include="match.cpp" />
<ClCompile Include="motw.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="pathfn.cpp" />
<ClCompile Include="qopen.cpp" />
<ClCompile Include="rar.cpp" />
<ClCompile Include="rarpch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rarvm.cpp" />
<ClCompile Include="rawread.cpp" />
<ClCompile Include="rdwrfn.cpp" />
<ClCompile Include="recvol.cpp" />
<ClCompile Include="resource.cpp" />
<ClCompile Include="rijndael.cpp" />
<ClCompile Include="rs.cpp" />
<ClCompile Include="rs16.cpp" />
<ClCompile Include="scantree.cpp" />
<ClCompile Include="secpassword.cpp" />
<ClCompile Include="sha1.cpp" />
<ClCompile Include="sha256.cpp" />
<ClCompile Include="smallfn.cpp" />
<ClCompile Include="strfn.cpp" />
<ClCompile Include="strlist.cpp" />
<ClCompile Include="system.cpp" />
<ClCompile Include="threadpool.cpp" />
<ClCompile Include="timefn.cpp" />
<ClCompile Include="ui.cpp" />
<ClCompile Include="unicode.cpp" />
<ClCompile Include="unpack.cpp" />
<ClCompile Include="volume.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+422
View File
@@ -0,0 +1,422 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="release_nocrypt|Win32">
<Configuration>release_nocrypt</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="release_nocrypt|x64">
<Configuration>release_nocrypt</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectName>UnRAR</ProjectName>
<ProjectGuid>{E815C46C-36C4-499F-BBC2-E772C6B17971}</ProjectGuid>
<RootNamespace>UnRAR</RootNamespace>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
<LinkIncremental>true</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
<LinkIncremental>true</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
<LinkIncremental>false</LinkIncremental>
<GenerateManifest>true</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>Cdecl</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>false</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CallingConvention>StdCall</CallingConvention>
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<OutputFile>$(OutDir)unrar.dll</OutputFile>
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration />
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="archive.cpp" />
<ClCompile Include="arcread.cpp" />
<ClCompile Include="blake2s.cpp" />
<ClCompile Include="cmddata.cpp" />
<ClCompile Include="consio.cpp" />
<ClCompile Include="crc.cpp" />
<ClCompile Include="crypt.cpp" />
<ClCompile Include="dll.cpp" />
<ClCompile Include="encname.cpp" />
<ClCompile Include="errhnd.cpp" />
<ClCompile Include="extinfo.cpp" />
<ClCompile Include="extract.cpp" />
<ClCompile Include="filcreat.cpp" />
<ClCompile Include="file.cpp" />
<ClCompile Include="filefn.cpp" />
<ClCompile Include="filestr.cpp" />
<ClCompile Include="find.cpp" />
<ClCompile Include="getbits.cpp" />
<ClCompile Include="global.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="hash.cpp" />
<ClCompile Include="headers.cpp" />
<ClCompile Include="isnt.cpp" />
<ClCompile Include="largepage.cpp" />
<ClCompile Include="match.cpp" />
<ClCompile Include="motw.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="pathfn.cpp" />
<ClCompile Include="qopen.cpp" />
<ClCompile Include="rar.cpp" />
<ClCompile Include="rarpch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rarvm.cpp" />
<ClCompile Include="rawread.cpp" />
<ClCompile Include="rdwrfn.cpp" />
<ClCompile Include="rijndael.cpp" />
<ClCompile Include="rs.cpp" />
<ClCompile Include="rs16.cpp" />
<ClCompile Include="scantree.cpp" />
<ClCompile Include="secpassword.cpp" />
<ClCompile Include="sha1.cpp" />
<ClCompile Include="sha256.cpp" />
<ClCompile Include="smallfn.cpp" />
<ClCompile Include="strfn.cpp" />
<ClCompile Include="strlist.cpp" />
<ClCompile Include="system.cpp" />
<ClCompile Include="threadpool.cpp" />
<ClCompile Include="timefn.cpp" />
<ClCompile Include="ui.cpp" />
<ClCompile Include="unicode.cpp" />
<ClCompile Include="unpack.cpp" />
<ClCompile Include="volume.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="rar.hpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="dll.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+59
View File
@@ -0,0 +1,59 @@
# Vendored: unrar 7.20.1 (RARLab UnRAR source)
Replaces `dmc_unrar` (see git history under `third_party/unrar/`) as the RAR
engine. This is the official Alexander Roshal UnRAR source, packaged on
GitHub by opello for easier vendoring.
## Source
- Upstream: https://github.com/opello/unrar — commit `97e1780310`
("v7.20.1: Extracted from https://www.rarlab.com/rar/unrarsrc-...", 2025-10-30)
- Version: 7.20.1 (see `version.hpp`: RARVER_MAJOR 7, RARVER_MINOR 20, BETA 1,
built 2025-10-28)
- Mirror of RARLab https://www.rarlab.com/rar_add.htm — this directory is a
verbatim copy of the upstream tree (159 files), no modifications except the
additions listed below.
## What this enables that dmc_unrar could not
- **RAR5 compression version 6** (WinRAR 6.x / 7.x archives, "v6:" algorithm
string). dmc_unrar 1.7.0 only dispatched `0x5000` (v5) in
`dmc_unrar_file_unpack`, so any WinRAR 6+ archive hit
`DMC_UNRAR_FILE_UNSUPPORTED_VERSION` and surfaced as "corrupt archive".
- **Encrypted RAR** (headers and/or entries) — password via `RARSetPassword`.
- **Multi-volume RAR** (`.partNN.rar` chains) — via open-mode + volume
callback handling.
- RAR4 (all sub-versions) continues to work.
## Build model
Compile the upstream DLL source set as a **static library**, define
`-DRARDLL` (suppresses `rar.cpp`'s `main()` via `#if !defined(RARDLL)`).
Source set (49 .cpp, matching `UnRARDll.vcxproj` ClCompile list):
archive arcread blake2s cmddata consio crc crypt dll encname errhnd extinfo
extract filcreat file filefn filestr find getbits global hash headers isnt
largepage match motw options pathfn qopen rar rarpch rarvm rawread rdwrfn
rijndael rs rs16 scantree secpassword sha1 sha256 smallfn strfn strlist
system threadpool timefn ui unicode unpack volume
Compile flags:
- `-std=c++17 -DRARDLL -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE`
- PS5 (`prospero-clang++`): the toolchain defaults to `-stdlib=libc++`
(FreeBSD-style sysroot; there is no GNU libstdc++). No explicit `-lc++`
needed. `_UNIX` is defined by the platform headers.
- Host tests (Windows/MinGW `g++`): libstdc++ default is fine.
- Windows link needs `-lpowrprof` (SystemSuspend in `system.cpp`);
`SetSuspendState` is referenced only on `_WIN_ALL`.
The upstream C API (`dll.hpp`) is C-compatible (extern "C" + plain C
structs). C translation units include `unrar_c_api.h` (platform shim +
re-export of the entry points used by `src/rar_extract.c`), never the unrar
C++ headers.
## Additions in this directory (not upstream)
- `unrar_c_api.h` — WFM facade shim (see file header).
- `VENDORED.md` — this file.
## License
UnRAR freeware license (`license.txt`): free to use in any software to
handle RAR archives; may NOT be used to build a RAR-compatible *archiver* or
re-create the RAR compression algorithm. Full text must stay with the
source. This replaces the GPL-2.0 dmc_unrar notice in THIRD_PARTY_NOTICES.
+59
View File
@@ -0,0 +1,59 @@
ACKNOWLEDGMENTS
* We used "Screaming Fast Galois Field Arithmetic Using Intel
SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
and Ethan L. Miller to improve Reed-Solomon coding performance.
Also we are grateful to Artem Drobanov and Bulat Ziganshin
for samples and ideas allowed to make Reed-Solomon coding
more efficient.
* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
and Dmitry Subbotin carryless rangecoder public domain source code.
You can find it in ftp.elf.stuba.sk/pub/pc/pack.
* RAR encryption includes parts of public domain code
from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
* With exception of SFX modules, RAR uses CRC32 function based
on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
is available here:
https://sourceforge.net/projects/slicing-by-8/
Original Intel Slicing-by-8 code is licensed under BSD License
available at http://www.opensource.org/licenses/bsd-license.html
Copyright (c) 2004-2006 Intel Corporation.
All Rights Reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
and Christian Winnerlein.
* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
to significantly improve RAR compression and speed.
+187
View File
@@ -0,0 +1,187 @@
static bool IsAnsiEscComment(const wchar *Data,size_t Size);
bool Archive::GetComment(std::wstring &CmtData)
{
if (!MainComment)
return false;
int64 SavePos=Tell();
bool Success=DoGetComment(CmtData);
Seek(SavePos,SEEK_SET);
return Success;
}
bool Archive::DoGetComment(std::wstring &CmtData)
{
#ifndef SFX_MODULE
uint CmtLength;
if (Format==RARFMT14)
{
Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
CmtLength=GetByte();
CmtLength+=(GetByte()<<8);
}
else
#endif
{
if (MainHead.CommentInHeader)
{
// Old style (RAR 2.9) archive comment embedded into the main
// archive header.
Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT)
return false;
}
else
{
// Current (RAR 3.0+) version of archive comment.
Seek(GetStartPos(),SEEK_SET);
if (SearchSubBlock(SUBHEAD_TYPE_CMT)!=0)
if (ReadCommentData(CmtData))
return true;
else
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
#ifndef SFX_MODULE
// Old style (RAR 2.9) comment header embedded into the main
// archive header.
if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD)
{
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD;
#endif
}
#ifndef SFX_MODULE
if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30)
{
if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
return false;
ComprDataIO DataIO;
DataIO.SetTestMode(true);
uint UnpCmtLength;
if (Format==RARFMT14)
{
#ifdef RAR_NOCRYPT
return false;
#else
UnpCmtLength=GetByte();
UnpCmtLength+=(GetByte()<<8);
if (CmtLength<2)
return false;
CmtLength-=2;
DataIO.SetCmt13Encryption();
CommHead.UnpVer=15;
#endif
}
else
UnpCmtLength=CommHead.UnpSize;
DataIO.SetFiles(this,NULL);
DataIO.EnableShowProgress(false);
DataIO.SetPackedSizeToRead(CmtLength);
DataIO.UnpHash.Init(HASH_CRC32,1);
DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet.
Unpack CmtUnpack(&DataIO);
CmtUnpack.Init(0x10000,false);
CmtUnpack.SetDestSize(UnpCmtLength);
CmtUnpack.DoUnpack(CommHead.UnpVer,false);
if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
{
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
else
{
byte *UnpData;
size_t UnpDataSize;
DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
if (UnpDataSize>0)
{
#ifdef _WIN_ALL
// If we ever decide to extend it to Android, we'll need to alloc
// 4x memory for OEM to UTF-8 output here.
OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
#endif
std::string UnpStr((char*)UnpData,UnpDataSize);
CharToWide(UnpStr,CmtData);
}
}
}
else
{
if (CmtLength==0)
return false;
std::vector<byte> CmtRaw(CmtLength);
int ReadSize=Read(CmtRaw.data(),CmtLength);
if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared.
{
CmtLength=ReadSize;
CmtRaw.resize(CmtLength);
}
if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
{
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
// CmtData.resize(CmtLength+1);
CmtRaw.push_back(0);
#ifdef _WIN_ALL
// If we ever decide to extend it to Android, we'll need to alloc
// 4x memory for OEM to UTF-8 output here.
OemToCharA((char *)CmtRaw.data(),(char *)CmtRaw.data());
#endif
CharToWide((const char *)CmtRaw.data(),CmtData);
// CmtData->resize(wcslen(CmtData->data()));
}
#endif
return CmtData.size() > 0;
}
bool Archive::ReadCommentData(std::wstring &CmtData)
{
std::vector<byte> CmtRaw;
if (!ReadSubData(&CmtRaw,NULL,false))
return false;
size_t CmtSize=CmtRaw.size();
CmtRaw.push_back(0);
// CmtData->resize(CmtSize+1);
if (Format==RARFMT50)
UtfToWide((char *)CmtRaw.data(),CmtData);
else
if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
{
CmtData=RawToWide(CmtRaw);
}
else
{
CharToWide((const char *)CmtRaw.data(),CmtData);
}
// CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length.
return true;
}
void Archive::ViewComment()
{
if (Cmd->DisableComment)
return;
std::wstring CmtBuf;
if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments.
{
size_t CmtSize=CmtBuf.size();
auto EndPos=CmtBuf.find(0x1A);
if (EndPos!=std::wstring::npos)
CmtSize=EndPos;
mprintf(St(MArcComment));
mprintf(L":\n");
OutComment(CmtBuf);
}
}
+361
View File
@@ -0,0 +1,361 @@
#include "rar.hpp"
#include "arccmt.cpp"
Archive::Archive(CommandData *InitCmd)
{
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
DummyCmd=(InitCmd==NULL);
Cmd=DummyCmd ? (new CommandData):InitCmd;
OpenShared=Cmd->OpenShared;
Format=RARFMT_NONE;
Solid=false;
Volume=false;
MainComment=false;
Locked=false;
Signed=false;
FirstVolume=false;
NewNumbering=false;
SFXSize=0;
LatestTime.Reset();
Protected=false;
Encrypted=false;
FailedHeaderDecryption=false;
BrokenHeader=false;
LastReadBlock=0;
CurHeaderType=HEAD_UNKNOWN;
CurBlockPos=0;
NextBlockPos=0;
RecoveryPercent=-1;
MainHead.Reset();
CryptHead={};
EndArcHead.Reset();
VolNumber=0;
VolWrite=0;
AddingFilesSize=0;
AddingHeadersSize=0;
Splitting=false;
NewArchive=false;
SilentOpen=false;
#ifdef USE_QOPEN
ProhibitQOpen=false;
#endif
}
Archive::~Archive()
{
if (DummyCmd)
delete Cmd;
}
void Archive::CheckArc(bool EnableBroken)
{
if (!IsArchive(EnableBroken))
{
// If FailedHeaderDecryption is set, we already reported that archive
// password is incorrect.
if (!FailedHeaderDecryption)
uiMsg(UIERROR_BADARCHIVE,FileName);
ErrHandler.Exit(RARX_BADARC);
}
}
#if !defined(SFX_MODULE)
void Archive::CheckOpen(const std::wstring &Name)
{
TOpen(Name);
CheckArc(false);
}
#endif
bool Archive::WCheckOpen(const std::wstring &Name)
{
if (!WOpen(Name))
return false;
if (!IsArchive(false))
{
uiMsg(UIERROR_BADARCHIVE,FileName);
Close();
return false;
}
return true;
}
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
{
RARFORMAT Type=RARFMT_NONE;
if (Size>=1 && D[0]==0x52)
#ifndef SFX_MODULE
if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
Type=RARFMT14;
else
#endif
if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
{
// We check the last signature byte, so we can return a sensible
// warning in case we'll want to change the archive format
// sometimes in the future.
#ifndef SFX_MODULE
if (D[6]==0)
Type=RARFMT15;
else
#endif
if (D[6]==1)
Type=RARFMT50;
else
if (D[6]>1 && D[6]<5)
Type=RARFMT_FUTURE;
}
return Type;
}
bool Archive::IsArchive(bool EnableBroken)
{
Encrypted=false;
BrokenHeader=false; // Might be left from previous volume.
#ifndef SFX_MODULE
if (IsDevice())
{
uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
return false;
}
#endif
if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
return false;
SFXSize=0;
RARFORMAT Type;
if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
{
Format=Type;
if (Format==RARFMT14)
Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
}
else
{
std::vector<char> Buffer(MAXSFXSIZE);
long CurPos=(long)Tell();
int ReadSize=Read(Buffer.data(),Buffer.size()-16);
for (int I=0;I<ReadSize;I++)
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
{
Format=Type;
if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
{
char *D=&Buffer[28-CurPos];
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
continue;
}
SFXSize=CurPos+I;
Seek(SFXSize,SEEK_SET);
if (Format==RARFMT15 || Format==RARFMT50)
Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
break;
}
if (SFXSize==0)
return false;
}
if (Format==RARFMT_FUTURE)
{
uiMsg(UIERROR_NEWRARFORMAT,FileName);
return false;
}
if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
{
if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
return false;
MarkHead.HeadSize=SIZEOF_MARKHEAD5;
}
else
MarkHead.HeadSize=SIZEOF_MARKHEAD3;
#ifdef RARDLL
// If callback function is not set, we cannot get the password,
// so we skip the initial header processing for encrypted header archive.
// It leads to skipped archive comment, but the rest of archive data
// is processed correctly.
if (Cmd->Callback==NULL)
SilentOpen=true;
#endif
bool HeadersLeft; // Any headers left to read.
bool StartFound=false; // Main or encryption headers found.
// Skip the archive encryption header if any and read the main header.
while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
{
SeekToNext();
HEADER_TYPE Type=GetHeaderType();
// In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
// avoid the password prompt.
StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
if (StartFound)
break;
}
// We should not do it for EnableBroken or we'll get 'not RAR archive'
// messages when extracting encrypted archives with wrong password.
if (FailedHeaderDecryption && !EnableBroken)
return false;
if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
{
if (!FailedHeaderDecryption) // If not reported a wrong password already.
uiMsg(UIERROR_MHEADERBROKEN,FileName);
if (!EnableBroken)
return false;
}
MainComment=MainHead.CommentInHeader;
// If we process non-encrypted archive or can request a password,
// we set 'first volume' flag based on file attributes below.
// It is necessary for RAR 2.x archives, which did not have 'first volume'
// flag in main header. Also for all RAR formats we need to scan until
// first file header to set "comment" flag when reading service header.
// Unless we are in silent mode, we need to know about presence of comment
// immediately after IsArchive call.
if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
{
int64 SavePos=Tell();
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
HEADER_TYPE SaveCurHeaderType=CurHeaderType;
while (ReadHeader()!=0)
{
HEADER_TYPE HeaderType=GetHeaderType();
if (HeaderType==HEAD_SERVICE)
{
// If we have a split service headers, it surely indicates non-first
// volume. But not split service header does not guarantee the first
// volume, because we can have split file after non-split archive
// comment. So we do not quit from loop here.
FirstVolume=Volume && !SubHead.SplitBefore;
}
else
if (HeaderType==HEAD_FILE)
{
FirstVolume=Volume && !FileHead.SplitBefore;
break;
}
else
if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
break;
SeekToNext();
}
CurBlockPos=SaveCurBlockPos;
NextBlockPos=SaveNextBlockPos;
CurHeaderType=SaveCurHeaderType;
Seek(SavePos,SEEK_SET);
}
if (!Volume || FirstVolume)
FirstVolumeName=FileName;
return true;
}
void Archive::SeekToNext()
{
Seek(NextBlockPos,SEEK_SET);
}
// Calculate the block size including encryption fields and padding if any.
uint Archive::FullHeaderSize(size_t Size)
{
if (Encrypted)
{
Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
if (Format == RARFMT50)
Size += SIZE_INITV;
else
Size += SIZE_SALT30;
}
return uint(Size);
}
#ifdef USE_QOPEN
bool Archive::Open(const std::wstring &Name,uint Mode)
{
// Important if we reuse Archive object and it has virtual QOpen
// file position not matching real. For example, for 'l -v volname'.
QOpen.Unload();
return File::Open(Name,Mode);
}
int Archive::Read(void *Data,size_t Size)
{
size_t Result;
if (QOpen.Read(Data,Size,Result))
return (int)Result;
return File::Read(Data,Size);
}
void Archive::Seek(int64 Offset,int Method)
{
if (!QOpen.Seek(Offset,Method))
File::Seek(Offset,Method);
}
int64 Archive::Tell()
{
int64 QPos;
if (QOpen.Tell(&QPos))
return QPos;
return File::Tell();
}
#endif
// Return 0 if dictionary size is invalid. If size is RAR7 only, return
// the adjusted nearest bottom value. Return header flags in Flags.
uint64 Archive::GetWinSize(uint64 Size,uint &Flags)
{
Flags=0;
// Allow 128 KB - 1 TB range.
if (Size<0x20000 || Size>0x10000000000ULL)
return 0;
uint64 Pow2=0x20000; // Power of 2 dictionary size.
for (;2*Pow2<=Size;Pow2*=2)
Flags+=FCI_DICT_BIT0;
if (Size==Pow2)
return Size; // If 'Size' is the power of 2, return it as is.
// Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'.
uint64 Fraction=(Size-Pow2)/(Pow2/32);
Flags+=(uint)Fraction*FCI_DICT_FRACT0;
return Pow2+Fraction*(Pow2/32);
}
+158
View File
@@ -0,0 +1,158 @@
#ifndef _RAR_ARCHIVE_
#define _RAR_ARCHIVE_
class PPack;
class RawRead;
class RawWrite;
enum NOMODIFY_FLAGS
{
NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
};
enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
enum ADDSUBDATA_FLAGS
{
ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
ASDF_COMPRESS = 2, // Allow to compress data following subheader.
ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
};
// RAR5 headers must not exceed 2 MB.
#define MAX_HEADER_SIZE_RAR5 0x200000
class Archive:public File
{
private:
void UpdateLatestTime(FileHeader *CurBlock);
void ConvertNameCase(std::wstring &Name);
void ConvertFileHeader(FileHeader *hd);
size_t ReadHeader14();
size_t ReadHeader15();
size_t ReadHeader50();
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
void RequestArcPassword(RarCheckPassword *SelPwd);
void UnexpEndArcMsg();
void BrokenHeaderMsg();
void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info);
bool DoGetComment(std::wstring &CmtData);
bool ReadCommentData(std::wstring &CmtData);
#if !defined(RAR_NOCRYPT)
CryptData HeadersCrypt;
#endif
ComprDataIO SubDataIO;
bool DummyCmd;
CommandData *Cmd;
int RecoveryPercent;
RarTime LatestTime;
int LastReadBlock;
HEADER_TYPE CurHeaderType;
bool SilentOpen;
#ifdef USE_QOPEN
QuickOpen QOpen;
bool ProhibitQOpen;
#endif
public:
Archive(CommandData *InitCmd=nullptr);
~Archive();
static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
size_t SearchBlock(HEADER_TYPE HeaderType);
size_t SearchSubBlock(const wchar *Type);
size_t SearchRR();
int GetRecoveryPercent() {return RecoveryPercent;}
size_t ReadHeader();
void CheckArc(bool EnableBroken);
void CheckOpen(const std::wstring &Name);
bool WCheckOpen(const std::wstring &Name);
bool GetComment(std::wstring &CmtData);
void ViewComment();
void SetLatestTime(RarTime *NewTime);
void SeekToNext();
bool CheckAccess();
bool IsArcDir();
void ConvertAttributes();
void VolSubtractHeaderSize(size_t SubSize);
uint FullHeaderSize(size_t Size);
int64 GetStartPos();
void AddSubData(const byte *SrcData,uint64 DataSize,File *SrcFile,
const wchar *Name,uint Flags);
bool ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode);
HEADER_TYPE GetHeaderType() {return CurHeaderType;}
CommandData* GetCommandData() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
#ifdef USE_QOPEN
bool Open(const std::wstring &Name,uint Mode=FMF_READ) override;
int Read(void *Data,size_t Size) override;
void Seek(int64 Offset,int Method) override;
int64 Tell() override;
void QOpenUnload() {QOpen.Unload();}
void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
#endif
static uint64 GetWinSize(uint64 Size,uint &Flags);
// Needed to see wstring based Open from File. Otherwise compiler finds
// Open in Archive and doesn't check the base class overloads.
using File::Open;
BaseBlock ShortBlock;
MarkHeader MarkHead;
MainHeader MainHead;
CryptHeader CryptHead;
FileHeader FileHead;
EndArcHeader EndArcHead;
SubBlockHeader SubBlockHead;
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
EAHeader EAHead;
StreamHeader StreamHead;
int64 CurBlockPos;
int64 NextBlockPos;
RARFORMAT Format;
bool Solid;
bool Volume;
bool MainComment;
bool Locked;
bool Signed;
bool FirstVolume;
bool NewNumbering;
bool Protected;
bool Encrypted;
size_t SFXSize;
bool BrokenHeader;
bool FailedHeaderDecryption;
#if !defined(RAR_NOCRYPT)
byte ArcSalt[SIZE_SALT50];
#endif
bool Splitting;
uint VolNumber;
int64 VolWrite;
// Total size of files adding to archive. Might also include the size of
// files repacked in solid archive.
uint64 AddingFilesSize;
uint64 AddingHeadersSize;
bool NewArchive;
std::wstring FirstVolumeName;
#ifdef PROPAGATE_MOTW
MarkOfTheWeb Motw;
#endif
};
#endif
+1553
View File
File diff suppressed because it is too large. Load diff
+179
View File
@@ -0,0 +1,179 @@
// Based on public domain code written in 2012 by Samuel Neves
#include "rar.hpp"
static const byte blake2s_sigma[10][16] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
};
#ifdef USE_SSE
#include "blake2s_sse.cpp"
#endif
static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
static void blake2s_final( blake2s_state *S, byte *digest );
#include "blake2sp.cpp"
static const uint32 blake2s_IV[8] =
{
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};
static inline void blake2s_set_lastnode( blake2s_state *S )
{
S->f[1] = ~0U;
}
/* Some helper functions, not necessarily useful */
static inline void blake2s_set_lastblock( blake2s_state *S )
{
if( S->last_node ) blake2s_set_lastnode( S );
S->f[0] = ~0U;
}
static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
{
S->t[0] += inc;
S->t[1] += ( S->t[0] < inc );
}
/* init2 xors IV with input parameter block */
void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
{
#ifdef USE_SSE
if (_SSE_Version>=SSE_SSE2)
blake2s_init_sse();
#endif
S->init(); // Clean data.
for( int i = 0; i < 8; ++i )
S->h[i] = blake2s_IV[i];
S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
S->h[2] ^= node_offset;
S->h[3] ^= (node_depth<<16)|0x20000000;
}
#define G(r,i,m,a,b,c,d) \
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
d = rotr32(d ^ a, 16); \
c = c + d; \
b = rotr32(b ^ c, 12); \
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
d = rotr32(d ^ a, 8); \
c = c + d; \
b = rotr32(b ^ c, 7);
static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
{
uint32 m[16];
uint32 v[16];
for( size_t i = 0; i < 16; ++i )
m[i] = RawGet4( block + i * 4 );
for( size_t i = 0; i < 8; ++i )
v[i] = S->h[i];
v[ 8] = blake2s_IV[0];
v[ 9] = blake2s_IV[1];
v[10] = blake2s_IV[2];
v[11] = blake2s_IV[3];
v[12] = S->t[0] ^ blake2s_IV[4];
v[13] = S->t[1] ^ blake2s_IV[5];
v[14] = S->f[0] ^ blake2s_IV[6];
v[15] = S->f[1] ^ blake2s_IV[7];
for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
{
G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
}
for( size_t i = 0; i < 8; ++i )
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
{
while( inlen > 0 )
{
size_t left = S->buflen;
size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
if( inlen > fill )
{
memcpy( S->buf + left, in, fill ); // Fill buffer
S->buflen += fill;
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
#ifdef USE_SSE
if (_SSE_Version>=SSE_SSSE3)
blake2s_compress_sse( S, S->buf );
else
blake2s_compress( S, S->buf ); // Compress
#else
blake2s_compress( S, S->buf ); // Compress
#endif
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
S->buflen -= BLAKE2S_BLOCKBYTES;
in += fill;
inlen -= fill;
}
else // inlen <= fill
{
memcpy( S->buf + left, in, (size_t)inlen );
S->buflen += (size_t)inlen; // Be lazy, do not compress
in += inlen;
inlen = 0;
}
}
}
void blake2s_final( blake2s_state *S, byte *digest )
{
if( S->buflen > BLAKE2S_BLOCKBYTES )
{
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
blake2s_compress( S, S->buf );
S->buflen -= BLAKE2S_BLOCKBYTES;
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
}
blake2s_increment_counter( S, ( uint32 )S->buflen );
blake2s_set_lastblock( S );
memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
blake2s_compress( S, S->buf );
for( int i = 0; i < 8; ++i ) /* Output full hash */
RawPut4( S->h[i], digest + 4 * i );
}
+104
View File
@@ -0,0 +1,104 @@
// Based on public domain code written in 2012 by Samuel Neves
#ifndef _RAR_BLAKE2_
#define _RAR_BLAKE2_
#define BLAKE2_DIGEST_SIZE 32
#define BLAKE2_THREADS_NUMBER 8
// Use constexpr instead of enums for -std=c++20 compatibility.
constexpr size_t BLAKE2S_BLOCKBYTES = 64;
constexpr size_t BLAKE2S_OUTBYTES = 32;
// Alignment to 64 improves performance of both SSE and non-SSE versions.
// Alignment to n*16 is required for SSE version, so we selected 64.
// We use the custom alignment scheme instead of __declspec(align(x)),
// because it is less compiler dependent. Also the compiler directive
// does not help if structure is a member of class allocated through
// 'new' operator.
struct blake2s_state
{
// Use constexpr instead of enums, because otherwise clang -std=c++20
// issues a warning about "arithmetic between different enumeration types"
// in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration.
static constexpr size_t BLAKE_ALIGNMENT = 64;
// buffer and uint32 h[8], t[2], f[2];
// 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip.
// It might differ in later versions.
static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES;
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
size_t buflen;
byte last_node;
blake2s_state()
{
set_pointers();
}
// Required when we declare and assign in the same command.
blake2s_state(blake2s_state &st)
{
set_pointers();
*this=st;
}
void set_pointers()
{
// Set aligned pointers. Must be done in constructor, not in Init(),
// so assignments like 'blake2sp_state res=blake2ctx' work correctly
// even if blake2sp_init is not called for 'res'.
buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
t = h + 8;
f = t + 2;
}
void init()
{
memset( ubuf, 0, sizeof( ubuf ) );
buflen = 0;
last_node = 0;
}
// Since we use pointers, the default = would work incorrectly.
blake2s_state& operator = (blake2s_state &st)
{
if (this != &st)
{
memcpy(buf, st.buf, BLAKE_DATA_SIZE);
buflen = st.buflen;
last_node = st.last_node;
}
return *this;
}
};
#ifdef RAR_SMP
class ThreadPool;
#endif
struct blake2sp_state
{
blake2s_state S[8];
blake2s_state R;
byte buf[8 * BLAKE2S_BLOCKBYTES];
size_t buflen;
#ifdef RAR_SMP
ThreadPool *ThPool;
uint MaxThreads;
#endif
};
void blake2sp_init( blake2sp_state *S );
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
void blake2sp_final( blake2sp_state *S, byte *digest );
#endif
+113
View File
@@ -0,0 +1,113 @@
// Based on public domain code written in 2012 by Samuel Neves
// Initialization vector.
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
// Constants for cyclic rotation.
static __m128i crotr8, crotr16;
#ifdef __GNUC__
__attribute__((target("sse2")))
#endif
static void blake2s_init_sse()
{
// We cannot initialize these 128 bit variables in place when declaring
// them globally, because global scope initialization is performed before
// our SSE check and it would make code incompatible with older non-SSE2
// CPUs. Also we cannot initialize them as static inside of function
// using these variables, because SSE static initialization is not thread
// safe: first thread starts initialization and sets "init done" flag even
// if it is not done yet, second thread can attempt to access half-init
// SSE data. So we moved init code here.
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
}
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
#define mm_rotr_epi32(r, c) ( \
c==8 ? _mm_shuffle_epi8(r,crotr8) \
: c==16 ? _mm_shuffle_epi8(r,crotr16) \
: _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
#define G1(row1,row2,row3,row4,buf) \
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
row4 = _mm_xor_si128( row4, row1 ); \
row4 = mm_rotr_epi32(row4, 16); \
row3 = _mm_add_epi32( row3, row4 ); \
row2 = _mm_xor_si128( row2, row3 ); \
row2 = mm_rotr_epi32(row2, 12);
#define G2(row1,row2,row3,row4,buf) \
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
row4 = _mm_xor_si128( row4, row1 ); \
row4 = mm_rotr_epi32(row4, 8); \
row3 = _mm_add_epi32( row3, row4 ); \
row2 = _mm_xor_si128( row2, row3 ); \
row2 = mm_rotr_epi32(row2, 7);
#define DIAGONALIZE(row1,row2,row3,row4) \
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
#define UNDIAGONALIZE(row1,row2,row3,row4) \
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
// and about the same in x64 mode in our test. Perhaps depends on compiler.
// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
// instructions here, but they did not show any speed gain on i7-6700K.
#define SSE_ROUND(m,row,r) \
{ \
__m128i buf; \
buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
G1(row[0],row[1],row[2],row[3],buf); \
buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
G2(row[0],row[1],row[2],row[3],buf); \
DIAGONALIZE(row[0],row[1],row[2],row[3]); \
buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
G1(row[0],row[1],row[2],row[3],buf); \
buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
G2(row[0],row[1],row[2],row[3],buf); \
UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
}
#ifdef __GNUC__
__attribute__((target("ssse3")))
#endif
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
{
__m128i row[4];
__m128i ff0, ff1;
const uint32 *m = ( uint32 * )block;
row[0] = ff0 = LOAD( &S->h[0] );
row[1] = ff1 = LOAD( &S->h[4] );
row[2] = blake2s_IV_0_3;
row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
SSE_ROUND( m, row, 0 );
SSE_ROUND( m, row, 1 );
SSE_ROUND( m, row, 2 );
SSE_ROUND( m, row, 3 );
SSE_ROUND( m, row, 4 );
SSE_ROUND( m, row, 5 );
SSE_ROUND( m, row, 6 );
SSE_ROUND( m, row, 7 );
SSE_ROUND( m, row, 8 );
SSE_ROUND( m, row, 9 );
STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
return 0;
}
+155
View File
@@ -0,0 +1,155 @@
/*
BLAKE2 reference source code package - reference C implementations
Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#define PARALLELISM_DEGREE 8
void blake2sp_init( blake2sp_state *S )
{
memset( S->buf, 0, sizeof( S->buf ) );
S->buflen = 0;
blake2s_init_param( &S->R, 0, 1 ); // Init root.
for( uint32 i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
S->R.last_node = 1;
S->S[PARALLELISM_DEGREE - 1].last_node = 1;
}
struct Blake2ThreadData
{
void Update();
blake2s_state *S;
const byte *in;
size_t inlen;
};
void Blake2ThreadData::Update()
{
size_t inlen__ = inlen;
const byte *in__ = ( const byte * )in;
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
{
#ifdef USE_SSE
// We gain 5% in i7 SSE mode by prefetching next data block.
if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
_mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
#endif
// We tried to _forceinline blake2s_update and blake2s_compress_sse,
// but it didn't improve performance.
blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
}
}
#ifdef RAR_SMP
THREAD_PROC(Blake2Thread)
{
Blake2ThreadData *td=(Blake2ThreadData *)Data;
td->Update();
}
#endif
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
{
size_t left = S->buflen;
size_t fill = sizeof( S->buf ) - left;
if( left && inlen >= fill )
{
memcpy( S->buf + left, in, fill );
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
in += fill;
inlen -= fill;
left = 0;
}
Blake2ThreadData btd_array[PARALLELISM_DEGREE];
#ifdef RAR_SMP
uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
ThreadNumber=4;
#else
uint ThreadNumber=1;
#endif
for (size_t id__=0;id__<PARALLELISM_DEGREE;)
{
for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++)
{
Blake2ThreadData *btd=btd_array+Thread;
btd->inlen = inlen;
btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
btd->S = &S->S[id__];
#ifdef RAR_SMP
if (ThreadNumber>1)
S->ThPool->AddTask(Blake2Thread,(void*)btd);
else
btd->Update();
#else
btd->Update();
#endif
id__++;
}
#ifdef RAR_SMP
if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
S->ThPool->WaitDone();
#endif // RAR_SMP
}
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
if( inlen > 0 )
memcpy( S->buf + left, in, (size_t)inlen );
S->buflen = left + (size_t)inlen;
}
void blake2sp_final( blake2sp_state *S, byte *digest )
{
byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
{
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
{
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
}
blake2s_final( &S->S[i], hash[i] );
}
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
blake2s_final( &S->R, digest );
}
+1330
View File
File diff suppressed because it is too large. Load diff
+103
View File
@@ -0,0 +1,103 @@
#ifndef _RAR_CMDDATA_
#define _RAR_CMDDATA_
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
// In Windows we implement our own command line parser to avoid replacing
// \" by " in standard parser. Such replacing corrupts destination paths
// like "dest path\" in extraction commands.
#define CUSTOM_CMDLINE_PARSER
#endif
#define DefaultStoreList L"7z;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst"
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1};
class CommandData:public RAROptions
{
private:
void ProcessSwitch(const wchar *Switch);
void BadSwitch(const wchar *Switch);
uint GetExclAttr(const wchar *Str,bool &Dir);
#if !defined(SFX_MODULE)
void SetTimeFilters(const wchar *Mod,bool Before,bool Age);
void SetStoreTimeMode(const wchar *S);
#endif
int64 GetModSize(const wchar *S,uint DefMultiplier);
bool FileLists;
bool NoMoreSwitches;
RAR_CMD_LIST_MODE ListMode;
bool BareOutput;
public:
CommandData();
void Init();
void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
void ParseArg(const wchar *ArgW);
void ParseDone();
void ParseEnvVar();
void ReadConfig();
void PreprocessArg(const wchar *Arg);
void ProcessSwitchesString(const std::wstring &Str);
void OutTitle();
void OutHelp(RAR_EXIT ExitCode);
bool IsSwitch(int Ch);
bool ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
static bool CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode);
bool ExclDirByAttr(uint FileAttr);
bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta);
bool SizeCheck(int64 Size);
bool AnyFiltersActive();
int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
bool Flags,std::wstring *MatchedArg);
void ProcessCommand();
void AddArcName(const std::wstring &Name);
bool GetArcName(wchar *Name,int MaxSize);
bool GetArcName(std::wstring &Name);
#ifndef SFX_MODULE
void ReportWrongSwitches(RARFORMAT Format);
#endif
void GetBriefMaskList(const std::wstring &Masks,StringList &Args);
std::wstring Command;
std::wstring ArcName;
std::wstring ExtrPath;
std::wstring TempPath;
std::wstring SFXModule;
std::wstring CommentFile;
std::wstring ArcPath; // For -ap<path>.
std::wstring ExclArcPath; // For -ep4<path> switch.
std::wstring LogName;
std::wstring EmailTo;
// Read data from stdin and store in archive under a name specified here
// when archiving. Read an archive from stdin if any non-empty string
// is specified here when extracting.
std::wstring UseStdin;
StringList FileArgs;
StringList ExclArgs;
StringList InclArgs;
StringList ArcNames;
StringList StoreArgs;
#ifdef PROPAGATE_MOTW
StringList MotwList; // Extensions to assign the mark of the web.
#endif
SecPassword Password;
std::vector<int64> NextVolSizes;
#ifdef RARDLL
std::wstring DllDestName;
#endif
};
#endif
+380
View File
@@ -0,0 +1,380 @@
// Return 'true' if we need to exclude the file from processing as result
// of -x switch. If CheckInclList is true, we also check the file against
// the include list created with -n switch.
bool CommandData::ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
{
if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
return true;
if (!CheckInclList || InclArgs.ItemsCount()==0)
return false;
if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
return false;
return true;
}
bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode)
{
std::wstring Name,FullName,CurMask;
ConvertPath(&CheckName,&Name);
Args->Rewind();
while (Args->GetString(CurMask))
{
#ifdef _WIN_ALL
// 2025.09.11: Unix allows DOS slashes as a part of file name, so we do not
// convert it for Unix. In Windows we wish -xdir\file and -xdir/file both
// to exclude the file.
UnixSlashToDos(CurMask,CurMask);
#endif
wchar LastMaskChar=GetLastChar(CurMask);
bool DirMask=IsPathDiv(LastMaskChar); // Mask for directories only.
if (Dir)
{
// CheckName is a directory.
if (DirMask)
{
// We process the directory and have the directory exclusion mask.
// So let's convert "mask\" to "mask" and process it normally.
CurMask.pop_back();
}
else
{
// This code doesn't allow to apply -n and -x wildcard masks without
// trailing slash to folders unless these masks are * and *.*.
// See the changes history below.
// 2023.03.26: Previously we removed this code completely to let
// 'rar a arc dir -ndir\path\*' include empty folders in 'path' too.
// But then we received an email from user not willing -x*.avi to
// exclude folders like dir.avi with non-avi files. Also rar.txt
// mentions that masks like *.avi exclude only files. Initially
// we wanted masks like -npath\* or -xpath\* to match the entire
// contents of path including empty folders and added the special
// check for "*" and "*.*". But this is not very straightforward,
// when *.* and *.avi are processed differently, especially taking
// into account that we can specify the exact folder name without
// wildcards to process it and masks like 'dir*\' can be used to
// exclude folders. So we decided to skip all usual wildcard masks
// for folders.
// 2023.11.22: We returned the special check for "*" and "*.*",
// because users expected 'rar a arc dir -xdir\*' to exclude
// everything including subfolders in 'dir'. For now we returned it
// both for -n and -x, but we can limit it to -x only if needed.
std::wstring Name=PointToName(CurMask);
if (IsWildcard(Name) && Name!=L"*" && Name!=L"*.*")
continue;
}
}
else
{
// If we process a file inside of directory excluded by "dirmask\".
// we want to exclude such file too. So we convert "dirmask\" to
// "dirmask\*". It is important for operations other than archiving
// with -x. When archiving with -x, directory matched by "dirmask\"
// is excluded from further scanning.
if (DirMask)
CurMask+=L"*";
}
#ifndef SFX_MODULE
if (CheckFullPath && IsFullPath(CurMask))
{
// We do not need to do the special "*\" processing here, because
// unlike the "else" part of this "if", now we convert names to full
// format, so they all include the path, which is matched by "*\"
// correctly. Moreover, removing "*\" from mask would break
// the comparison, because now all names have the path.
if (FullName.empty())
ConvertNameToFull(CheckName,FullName);
if (CmpName(CurMask,FullName,MatchMode))
return true;
}
else
#endif
{
std::wstring CurName=Name;
// Important to convert before "*\" check below, so masks like
// d:*\something are processed properly.
size_t MaskOffset=ConvertPath(&CurMask,nullptr);
std::wstring CmpMask=CurMask.substr(MaskOffset);
if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
{
// We want "*\name" to match 'name' not only in subdirectories,
// but also in the current directory. We convert the name
// from 'name' to '.\name' to be matched by "*\" part even if it is
// in current directory.
CurName=L'.';
CurName+=CPATHDIVIDER;
CurName+=Name;
}
if (CmpName(CmpMask,CurName,MatchMode))
return true;
}
}
return false;
}
#ifndef SFX_MODULE
// Now this function performs only one task and only in Windows version:
// it skips symlinks to directories if -e1024 switch is specified.
// Symlinks are skipped in ScanTree class, so their entire contents
// is skipped too. Without this function we would check the attribute
// only directly before archiving, so we would skip the symlink record,
// but not the contents of symlinked directory.
bool CommandData::ExclDirByAttr(uint FileAttr)
{
#ifdef _WIN_ALL
if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
(ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
return true;
#endif
return false;
}
#endif
#if !defined(SFX_MODULE)
void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age)
{
bool ModeOR=false,TimeMods=false;
const wchar *S=Mod;
// Check if any 'mca' modifiers are present, set OR mode if 'o' is present,
// skip modifiers and set S to beginning of time string. Be sure to check
// *S!=0, because termination 0 is a part of string for wcschr.
for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++)
if (*S=='o' || *S=='O')
ModeOR=true;
else
TimeMods=true;
if (!TimeMods) // Assume 'm' if no modifiers are specified.
Mod=L"m";
// Set the specified time for every modifier. Be sure to check *Mod!=0,
// because termination 0 is a part of string for wcschr. This check is
// important when we set Mod to "m" above.
for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++)
switch(toupperw(*Mod))
{
case 'M':
if (Before)
{
Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S);
FileMtimeBeforeOR=ModeOR;
}
else
{
Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S);
FileMtimeAfterOR=ModeOR;
}
break;
case 'C':
if (Before)
{
Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S);
FileCtimeBeforeOR=ModeOR;
}
else
{
Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S);
FileCtimeAfterOR=ModeOR;
}
break;
case 'A':
if (Before)
{
Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S);
FileAtimeBeforeOR=ModeOR;
}
else
{
Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S);
FileAtimeAfterOR=ModeOR;
}
break;
}
}
#endif
#ifndef SFX_MODULE
// Return 'true' if we need to exclude the file from processing.
bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
{
bool FilterOR=false;
if (FileMtimeBefore.IsSet()) // Filter present.
if (ftm>=FileMtimeBefore) // Condition not matched.
if (FileMtimeBeforeOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileMtimeBeforeOR)
return false; // Include file in OR mode.
if (FileMtimeAfter.IsSet()) // Filter present.
if (ftm<FileMtimeAfter) // Condition not matched.
if (FileMtimeAfterOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileMtimeAfterOR)
return false; // Include file in OR mode.
if (FileCtimeBefore.IsSet()) // Filter present.
if (ftc>=FileCtimeBefore) // Condition not matched.
if (FileCtimeBeforeOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileCtimeBeforeOR)
return false; // Include file in OR mode.
if (FileCtimeAfter.IsSet()) // Filter present.
if (ftc<FileCtimeAfter) // Condition not matched.
if (FileCtimeAfterOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileCtimeAfterOR)
return false; // Include file in OR mode.
if (FileAtimeBefore.IsSet()) // Filter present.
if (fta>=FileAtimeBefore) // Condition not matched.
if (FileAtimeBeforeOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileAtimeBeforeOR)
return false; // Include file in OR mode.
if (FileAtimeAfter.IsSet()) // Filter present.
if (fta<FileAtimeAfter) // Condition not matched.
if (FileAtimeAfterOR)
FilterOR=true; // Not matched OR filter is present.
else
return true; // Exclude file in AND mode.
else // Condition matched.
if (FileAtimeAfterOR)
return false; // Include file in OR mode.
return FilterOR; // Exclude if all OR filters are not matched.
}
#endif
#ifndef SFX_MODULE
// Return 'true' if we need to exclude the file from processing.
bool CommandData::SizeCheck(int64 Size)
{
if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size.
return false;
if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
return true;
if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
return true;
return false;
}
#endif
// Return 0 if file must not be processed or a number of matched parameter otherwise.
int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
bool Flags,std::wstring *MatchedArg)
{
if (MatchedArg!=NULL)
MatchedArg->clear();
bool Dir=FileHead.Dir;
if (ExclCheck(FileHead.FileName,Dir,false,true))
return 0;
#ifndef SFX_MODULE
if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime))
return 0;
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
return 0;
if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
(!FileHead.Dir || !InclDir))
return 0;
if (!Dir && SizeCheck(FileHead.UnpSize))
return 0;
#endif
std::wstring ArgName;
FileArgs.Rewind();
for (int StringCount=1;FileArgs.GetString(ArgName);StringCount++)
{
// Ensure that both parameters of CmpName are either C++ strings or
// pointers, so we avoid time consuming string construction for one of
// parameters in this expensive loop.
if (CmpName(ArgName,FileHead.FileName,MatchType))
{
if (ExactMatch!=NULL)
*ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
if (MatchedArg!=NULL)
*MatchedArg=ArgName;
return StringCount;
}
}
return 0;
}
#if !defined(SFX_MODULE)
void CommandData::SetStoreTimeMode(const wchar *S)
{
if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+')
{
// Apply -ts, -ts1, -ts-, -ts+ to all 3 times.
// Handle obsolete -ts[2,3,4] as ts+.
EXTTIME_MODE Mode=EXTTIME_MAX;
if (*S=='-')
Mode=EXTTIME_NONE;
if (*S=='1')
Mode=EXTTIME_1S;
xmtime=xctime=xatime=Mode;
S++;
}
while (*S!=0)
{
EXTTIME_MODE Mode=EXTTIME_MAX;
if (S[1]=='-')
Mode=EXTTIME_NONE;
if (S[1]=='1')
Mode=EXTTIME_1S;
switch(toupperw(*S))
{
case 'M':
xmtime=Mode;
break;
case 'C':
xctime=Mode;
break;
case 'A':
xatime=Mode;
break;
case 'P':
PreserveAtime=true;
break;
}
S++;
}
}
#endif
+125
View File
@@ -0,0 +1,125 @@
void CommandData::OutTitle()
{
if (BareOutput || DisableCopyright)
return;
#if defined(__GNUC__) && defined(SFX_MODULE)
mprintf(St(MCopyrightS));
#else
#ifndef SILENT
static bool TitleShown=false;
if (TitleShown)
return;
TitleShown=true;
wchar Version[80];
if (RARVER_BETA!=0)
swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
else
swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
#if defined(_WIN_32) || defined(_WIN_64)
wcsncatz(Version,L" ",ASIZE(Version));
#endif
#ifdef _WIN_32
wcsncatz(Version,St(Mx86),ASIZE(Version));
#endif
#ifdef _WIN_64
wcsncatz(Version,St(Mx64),ASIZE(Version));
#endif
if (PrintVersion)
{
mprintf(L"%s",Version);
exit(0);
}
mprintf(St(MUCopyright),Version,RARVER_YEAR);
#endif
#endif
}
inline bool CmpMSGID(MSGID i1,MSGID i2)
{
#ifdef MSGID_INT
return i1==i2;
#else
// If MSGID is const char*, we cannot compare pointers only.
// Pointers to different instances of same string can differ,
// so we need to compare complete strings.
return wcscmp(i1,i2)==0;
#endif
}
void CommandData::OutHelp(RAR_EXIT ExitCode)
{
#if !defined(SILENT)
OutTitle();
static MSGID Help[]={
#ifdef SFX_MODULE
// Console SFX switches definition.
MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
#else
// UnRAR switches definition.
MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP,
MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwMLP,
MCHelpSwN,MCHelpSwNa,MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,
MCHelpSwOM,MCHelpSwOP,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,MCHelpSwR,
MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwTA,
MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,
MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY
#endif
};
for (uint I=0;I<ASIZE(Help);I++)
{
#ifndef SFX_MODULE
if (CmpMSGID(Help[I],MCHelpSwV))
continue;
#ifndef _WIN_ALL
static MSGID Win32Only[]={
MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
MCHelpSwEP2,MCHelpSwMLP,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
};
bool Found=false;
for (uint J=0;J<ASIZE(Win32Only);J++)
if (CmpMSGID(Help[I],Win32Only[J]))
{
Found=true;
break;
}
if (Found)
continue;
#endif
#ifdef _UNIX
if (CmpMSGID(Help[I],MRARTitle2))
{
mprintf(St(MFwrSlTitle2));
continue;
}
#endif
#if !defined(_UNIX) && !defined(_WIN_ALL)
if (CmpMSGID(Help[I],MCHelpSwOW))
continue;
#endif
#ifndef _WIN_ALL
if (CmpMSGID(Help[I],MCHelpSwAC))
continue;
#endif
#ifndef SAVE_LINKS
if (CmpMSGID(Help[I],MCHelpSwOL))
continue;
#endif
#ifndef RAR_SMP
if (CmpMSGID(Help[I],MCHelpSwMT))
continue;
#endif
#endif
mprintf(St(Help[I]));
}
mprintf(L"\n");
ErrHandler.Exit(ExitCode);
#endif
}
+48
View File
@@ -0,0 +1,48 @@
inline byte RangeCoder::GetChar()
{
return UnpackRead->GetChar();
}
void RangeCoder::InitDecoder(Unpack *UnpackRead)
{
RangeCoder::UnpackRead=UnpackRead;
low=code=0;
range=0xffffffff;
for (uint i = 0; i < 4; i++)
code=(code << 8) | GetChar();
}
// (int) cast before "low" added only to suppress compiler warnings.
#define ARI_DEC_NORMALIZE(code,low,range,read) \
{ \
while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \
{ \
code=(code << 8) | read->GetChar(); \
range <<= 8; \
low <<= 8; \
} \
}
inline int RangeCoder::GetCurrentCount()
{
return (code-low)/(range /= SubRange.scale);
}
inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT)
{
return (code-low)/(range >>= SHIFT);
}
inline void RangeCoder::Decode()
{
low += range*SubRange.LowCount;
range *= SubRange.HighCount-SubRange.LowCount;
}
+23
View File
@@ -0,0 +1,23 @@
/****************************************************************************
* Contents: 'Carryless rangecoder' by Dmitry Subbotin *
****************************************************************************/
class RangeCoder
{
public:
void InitDecoder(Unpack *UnpackRead);
inline int GetCurrentCount();
inline uint GetCurrentShiftCount(uint SHIFT);
inline void Decode();
inline void PutChar(unsigned int c);
inline byte GetChar();
uint low, code, range;
struct SUBRANGE
{
uint LowCount, HighCount, scale;
} SubRange;
Unpack *UnpackRead;
};
+61
View File
@@ -0,0 +1,61 @@
#ifndef _RAR_COMPRESS_
#define _RAR_COMPRESS_
// Combine pack and unpack constants to class to avoid polluting global
// namespace with numerous short names.
class PackDef
{
public:
// Maximum LZ match length we can encode even for short distances.
static const uint MAX_LZ_MATCH = 0x1001;
// We increment LZ match length for longer distances, because shortest
// matches are not allowed for them. Maximum length increment is 3
// for distances larger than 256KB (0x40000). Here we define the maximum
// incremented LZ match. Normally packer does not use it, but we must be
// ready to process it in corrupt archives.
static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3;
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3;
static const uint LOW_DIST_REP_COUNT = 16;
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
static const uint DCB = 64; // Base distance codes up to 4 GB.
static const uint DCX = 80; // Extended distance codes up to 1 TB.
static const uint LDC = 16;
static const uint RC = 44;
static const uint HUFF_TABLE_SIZEB = NC + DCB + RC + LDC;
static const uint HUFF_TABLE_SIZEX = NC + DCX + RC + LDC;
static const uint BC = 20;
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
static const uint DC30 = 60;
static const uint LDC30 = 17;
static const uint RC30 = 28;
static const uint BC30 = 20;
static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30;
static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
static const uint DC20 = 48;
static const uint RC20 = 28;
static const uint BC20 = 19;
static const uint MC20 = 257;
// Largest alphabet size among all values listed above.
static const uint LARGEST_TABLE_SIZE = 306;
};
enum FilterType {
// These values must not be changed, because we use them directly
// in RAR5 compression and decompression code.
FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_TEXT,
// These values can be changed.
FILTER_LONGRANGE,FILTER_EXHAUSTIVE,FILTER_NONE
};
#endif
+416
View File
@@ -0,0 +1,416 @@
#include "rar.hpp"
#include "log.cpp"
static MESSAGE_TYPE MsgStream=MSG_STDOUT;
static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
static bool ProhibitInput=false;
static bool ConsoleOutputPresent=false;
static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
#ifdef _WIN_ALL
static bool IsRedirected(DWORD nStdHandle)
{
HANDLE hStd=GetStdHandle(nStdHandle);
DWORD Mode;
return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
}
#endif
void InitConsole()
{
#ifdef _WIN_ALL
// We want messages like file names or progress percent to be printed
// immediately. Use only in Windows, in Unix they can cause wprintf %ls
// to fail with non-English strings.
setbuf(stdout,NULL);
setbuf(stderr,NULL);
// Detect if output is redirected and set output mode properly.
// We do not want to send Unicode output to files and especially to pipes
// like '|more', which cannot handle them correctly in Windows.
// In Unix console output is UTF-8 and it is handled correctly
// when redirecting, so no need to perform any adjustments.
StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
#ifdef _MSC_VER
if (!StdoutRedirected)
_setmode(_fileno(stdout), _O_U16TEXT);
if (!StderrRedirected)
_setmode(_fileno(stderr), _O_U16TEXT);
#endif
#elif defined(_UNIX)
StdoutRedirected=!isatty(fileno(stdout));
StderrRedirected=!isatty(fileno(stderr));
StdinRedirected=!isatty(fileno(stdin));
#endif
}
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream)
{
::MsgStream=MsgStream;
}
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)
{
::RedirectCharset=RedirectCharset;
}
void ProhibitConsoleInput()
{
ProhibitInput=true;
}
#ifndef SILENT
static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
{
ConsoleOutputPresent=true;
// No need for PrintfPrepareFmt here, vwstrprintf calls it.
std::wstring s=vwstrprintf(fmt,arglist);
ReplaceEsc(s);
#ifdef _WIN_ALL
if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
{
HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
DWORD Written;
if (RedirectCharset==RCH_UNICODE)
WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL);
else
{
// Avoid Unicode for redirect in Windows, it does not work with pipes.
std::string MsgA;
if (RedirectCharset==RCH_UTF8)
WideToUtf(s,MsgA);
else
WideToChar(s,MsgA);
if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding.
// We already converted \n to \r\n above, so we use WriteFile instead
// of C library to avoid unnecessary additional conversion.
WriteFile(hOut,MsgA.data(),(DWORD)MsgA.size(),&Written,NULL);
}
return;
}
// MSVC2008 vfwprintf writes every character to console separately
// and it is too slow. We use direct WriteConsole call instead.
HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
DWORD Written;
WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL);
#else
fputws(s.c_str(),dest);
// We do not use setbuf(NULL) in Unix (see comments in InitConsole).
fflush(dest);
#endif
}
void mprintf(const wchar *fmt,...)
{
if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
return;
fflush(stderr); // Ensure proper message order.
va_list arglist;
va_start(arglist,fmt);
FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
cvt_wprintf(dest,fmt,arglist);
va_end(arglist);
}
#endif
#ifndef SILENT
void eprintf(const wchar *fmt,...)
{
if (MsgStream==MSG_NULL)
return;
fflush(stdout); // Ensure proper message order.
va_list arglist;
va_start(arglist,fmt);
cvt_wprintf(stderr,fmt,arglist);
va_end(arglist);
}
#endif
#ifndef SILENT
static void QuitIfInputProhibited()
{
// We cannot handle user prompts if -si is used to read file or archive data
// from stdin.
if (ProhibitInput)
{
mprintf(St(MStdinNoInput));
ErrHandler.Exit(RARX_FATAL);
}
}
static void GetPasswordText(std::wstring &Str)
{
QuitIfInputProhibited();
if (StdinRedirected)
getwstr(Str); // Read from pipe or redirected file.
else
{
#ifdef _WIN_ALL
HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
DWORD ConInMode;
GetConsoleMode(hConIn,&ConInMode);
SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT.
std::vector<wchar> Buf(MAXPASSWORD);
// We prefer ReadConsole to ReadFile, so we can read Unicode input.
DWORD Read=0;
ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL);
Buf[Read]=0;
Str=Buf.data();
cleandata(Buf.data(),Buf.size()*sizeof(Buf[0]));
SetConsoleMode(hConIn,ConInMode);
// 2023.03.12: Previously we checked for presence of "\n" in entered
// passwords, supposing that truncated strings do not include it.
// We did it to read the rest of excessively long string, so it is not
// read later as the second password for -p switch. But this "\n" check
// doesn't seem to work in Windows 10 anymore and "\r" is present even
// in truncated strings. Also we increased MAXPASSWORD, so it is larger
// than MAXPASSWORD_RAR. Thus we removed this check as not working
// and not that necessary. Low level FlushConsoleInputBuffer doesn't help
// for high level ReadConsole, which in line input mode seems to store
// the rest of string in its own internal buffer.
#else
std::vector<char> StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters.
#ifdef __VMS
fgets(StrA.data(),StrA.size()-1,stdin);
#elif defined(__sun)
strncpyz(StrA.data(),getpassphrase(""),StrA.size());
#else
strncpyz(StrA.data(),getpass(""),StrA.size());
#endif
CharToWide(StrA.data(),Str);
cleandata(StrA.data(),StrA.size()*sizeof(StrA[0]));
#endif
}
RemoveLF(Str);
}
#endif
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password)
{
if (!StdinRedirected)
uiAlarm(UIALARM_QUESTION);
while (true)
{
// if (!StdinRedirected)
if (Type==UIPASSWORD_GLOBAL)
eprintf(L"\n%s: ",St(MAskPsw));
else
eprintf(St(MAskPswFor),FileName.c_str());
std::wstring PlainPsw;
GetPasswordText(PlainPsw);
if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL)
return false;
if (PlainPsw.size()>=MAXPASSWORD)
{
PlainPsw.erase(MAXPASSWORD-1);
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
}
if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
{
eprintf(St(MReAskPsw));
std::wstring CmpStr;
GetPasswordText(CmpStr);
if (CmpStr.empty() || PlainPsw!=CmpStr)
{
eprintf(St(MNotMatchPsw));
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
continue;
}
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
}
Password->Set(PlainPsw.c_str());
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
break;
}
return true;
}
#endif
#ifndef SILENT
void getwstr(std::wstring &str)
{
// Print buffered prompt title function before waiting for input.
fflush(stderr);
QuitIfInputProhibited();
str.clear();
const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name.
#if defined(_WIN_ALL)
// fgetws does not work well with non-English text in Windows,
// so we do not use it.
if (StdinRedirected) // ReadConsole does not work if redirected.
{
// fgets does not work well with pipes in Windows in our test.
// Let's use files.
std::vector<char> StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t.
File SrcFile;
SrcFile.SetHandleType(FILE_HANDLESTD);
SrcFile.SetLineInputMode(true);
int ReadSize=SrcFile.Read(&StrA[0],StrA.size()-1);
if (ReadSize<=0)
{
// Looks like stdin is a null device. We can enter to infinite loop
// calling Ask() or set an empty password, so let's better exit.
ErrHandler.ReadError(L"stdin");
}
StrA[ReadSize]=0;
// We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR,
// such as send passwords, we get OEM encoding by default, unless we
// use "chcp" in console. But we avoid OEM to ANSI conversion,
// because we also want to handle ANSI files redirection correctly,
// like "rar ... < ansifile.txt".
CharToWide(&StrA[0],str);
cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords.
}
else
{
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
DWORD SizeToRead=(DWORD)Buf.size()-1;
// ReadConsole fails in Windows 7 for requested input exceeding 30 KB.
// Not certain about Windows 8, so check for Windows 10 here.
if (WinNT()<=WNT_W10)
SizeToRead=Min(SizeToRead,0x4000);
DWORD ReadSize=0;
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0)
ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort.
Buf[ReadSize]=0;
str=Buf.data();
}
#else
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
if (fgetws(&Buf[0],Buf.size(),stdin)==NULL)
ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop.
str=Buf.data();
#endif
RemoveLF(str);
}
#endif
#ifndef SILENT
// We allow this function to return 0 in case of invalid input,
// because it might be convenient to press Enter to some not dangerous
// prompts like "insert disk with next volume". We should call this function
// again in case of 0 in dangerous prompt such as overwriting file.
int Ask(const wchar *AskStr)
{
uiAlarm(UIALARM_QUESTION);
const uint MaxItems=10;
wchar Item[MaxItems][40];
uint ItemKeyPos[MaxItems],NumItems=0;
for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_'))
{
wchar *CurItem=Item[NumItems];
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
wchar *EndItem=wcschr(CurItem,'_');
if (EndItem!=nullptr)
*EndItem=0;
uint KeyPos=0,CurKey;
while ((CurKey=CurItem[KeyPos])!=0)
{
bool Found=false;
for (uint I=0;I<NumItems && !Found;I++)
if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
Found=true;
if (!Found && CurKey!=' ')
break;
KeyPos++;
}
ItemKeyPos[NumItems]=KeyPos;
NumItems++;
}
for (uint I=0;I<NumItems;I++)
{
eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", ");
uint KeyPos=ItemKeyPos[I];
for (uint J=0;J<KeyPos;J++)
eprintf(L"%c",Item[I][J]);
eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
}
eprintf(L" ");
std::wstring Str;
getwstr(Str);
wchar Ch=toupperw(Str[0]);
for (uint I=0;I<NumItems;I++)
if (Ch==Item[I][ItemKeyPos[I]])
return I+1;
return 0;
}
#endif
static bool IsCommentUnsafe(const std::wstring &Data)
{
for (size_t I=0;I<Data.size();I++)
if (Data[I]==27 && Data[I+1]=='[')
for (size_t J=I+2;J<Data.size();J++)
{
// Return true for <ESC>[{key};"{string}"p used to redefine
// a keyboard key on some terminals.
if (Data[J]=='\"')
return true;
if (!IsDigit(Data[J]) && Data[J]!=';')
break;
}
return false;
}
void OutComment(const std::wstring &Comment)
{
if (IsCommentUnsafe(Comment))
return;
const size_t MaxOutSize=0x400;
for (size_t I=0;I<Comment.size();I+=MaxOutSize)
{
size_t CopySize=Min(MaxOutSize,Comment.size()-I);
mprintf(L"%s",Comment.substr(I,CopySize).c_str());
}
mprintf(L"\n");
}
bool IsConsoleOutputPresent()
{
return ConsoleOutputPresent;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef _RAR_CONSIO_
#define _RAR_CONSIO_
void InitConsole();
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream);
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset);
void ProhibitConsoleInput();
void OutComment(const std::wstring &Comment);
bool IsConsoleOutputPresent();
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password);
#endif
#ifdef SILENT
inline void mprintf(const wchar *fmt,...) {}
inline void eprintf(const wchar *fmt,...) {}
inline void Alarm() {}
inline int Ask(const wchar *AskStr) {return 0;}
inline void getwstr(std::wstring &str) {}
#else
void mprintf(const wchar *fmt,...);
void eprintf(const wchar *fmt,...);
void Alarm();
int Ask(const wchar *AskStr);
void getwstr(std::wstring &str);
#endif
#endif
+280
View File
@@ -0,0 +1,280 @@
// This CRC function is based on Intel Slicing-by-8 algorithm.
//
// Original Intel Slicing-by-8 code is available here:
//
// http://sourceforge.net/projects/slicing-by-8/
//
// Original Intel Slicing-by-8 code is licensed as:
//
// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
//
// This software program is licensed subject to the BSD License,
// available at http://www.opensource.org/licenses/bsd-license.html
#include "rar.hpp"
#ifndef SFX_MODULE
// User suggested to avoid BSD license in SFX module, so they do not need
// to include the license to SFX archive.
#define USE_SLICING
#endif
static uint crc_tables[16][256]; // Tables for Slicing-by-16.
#ifdef USE_NEON_CRC32
static bool CRC_Neon;
#endif
// Build the classic CRC32 lookup table.
// We also provide this function to legacy RAR and ZIP decryption code.
void InitCRC32(uint *CRCTab)
{
if (CRCTab[1]!=0)
return;
for (uint I=0;I<256;I++)
{
uint C=I;
for (uint J=0;J<8;J++)
C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
CRCTab[I]=C;
}
#ifdef USE_NEON_CRC32
#ifdef _APPLE
// getauxval isn't available in OS X
uint Value=0;
size_t Size=sizeof(Value);
int RetCode=sysctlbyname("hw.optional.armv8_crc32",&Value,&Size,NULL,0);
CRC_Neon=RetCode==0 && Value!=0;
#else
CRC_Neon=(getauxval(AT_HWCAP) & HWCAP_CRC32)!=0;
#endif
#endif
}
static void InitTables()
{
InitCRC32(crc_tables[0]);
#ifdef USE_SLICING
for (uint I=0;I<256;I++) // Build additional lookup tables.
{
uint C=crc_tables[0][I];
for (uint J=1;J<16;J++)
{
C=crc_tables[0][(byte)C]^(C>>8);
crc_tables[J][I]=C;
}
}
#endif
}
struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
uint CRC32(uint StartCRC,const void *Addr,size_t Size)
{
byte *Data=(byte *)Addr;
#ifdef USE_NEON_CRC32
if (CRC_Neon)
{
for (;Size>=8;Size-=8,Data+=8)
#ifdef __clang__
StartCRC = __builtin_arm_crc32d(StartCRC, RawGet8(Data));
#else
StartCRC = __builtin_aarch64_crc32x(StartCRC, RawGet8(Data));
#endif
for (;Size>0;Size--,Data++) // Process left data.
#ifdef __clang__
StartCRC = __builtin_arm_crc32b(StartCRC, *Data);
#else
StartCRC = __builtin_aarch64_crc32b(StartCRC, *Data);
#endif
return StartCRC;
}
#endif
#ifdef USE_SLICING
// Align Data to 16 for better performance and to avoid ALLOW_MISALIGNED
// check below.
for (;Size>0 && ((size_t)Data & 15)!=0;Size--,Data++)
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
// 2023.12.06: We switched to slicing-by-16, which seems to be faster than
// slicing-by-8 on modern CPUs. Slicing-by-32 would require 32 KB for tables
// and could be limited by L1 cache size on some CPUs.
for (;Size>=16;Size-=16,Data+=16)
{
#ifdef BIG_ENDIAN
StartCRC ^= RawGet4(Data);
uint D1 = RawGet4(Data+4);
uint D2 = RawGet4(Data+8);
uint D3 = RawGet4(Data+12);
#else
// We avoid RawGet4 here for performance reason, to access uint32
// directly even if ALLOW_MISALIGNED isn't defined. We can do it,
// because we aligned 'Data' above.
StartCRC ^= *(uint32 *) Data;
uint D1 = *(uint32 *) (Data+4);
uint D2 = *(uint32 *) (Data+8);
uint D3 = *(uint32 *) (Data+12);
#endif
StartCRC = crc_tables[15][(byte) StartCRC ] ^
crc_tables[14][(byte)(StartCRC >> 8) ] ^
crc_tables[13][(byte)(StartCRC >> 16)] ^
crc_tables[12][(byte)(StartCRC >> 24)] ^
crc_tables[11][(byte) D1 ] ^
crc_tables[10][(byte)(D1 >> 8) ] ^
crc_tables[ 9][(byte)(D1 >> 16)] ^
crc_tables[ 8][(byte)(D1 >> 24)] ^
crc_tables[ 7][(byte) D2 ] ^
crc_tables[ 6][(byte)(D2 >> 8)] ^
crc_tables[ 5][(byte)(D2 >> 16)] ^
crc_tables[ 4][(byte)(D2 >> 24)] ^
crc_tables[ 3][(byte) D3 ] ^
crc_tables[ 2][(byte)(D3 >> 8)] ^
crc_tables[ 1][(byte)(D3 >> 16)] ^
crc_tables[ 0][(byte)(D3 >> 24)];
}
#endif
for (;Size>0;Size--,Data++) // Process left data.
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
return StartCRC;
}
#ifndef SFX_MODULE
// For RAR 1.4 archives in case somebody still has them.
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
{
byte *Data=(byte *)Addr;
for (size_t I=0;I<Size;I++)
{
StartCRC=(StartCRC+Data[I])&0xffff;
StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff;
}
return StartCRC;
}
#endif
#if 0
static void TestCRC();
struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC;
void TestCRC()
{
// This function is invoked from global object and _SSE_Version is global
// and can be initialized after this function. So we explicitly initialize
// it here to enable SSE support in Blake2sp.
_SSE_Version=GetSSEVersion();
const uint FirstSize=300;
byte b[FirstSize];
if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84)
mprintf(L"\nCRC32 test1 OK");
else
mprintf(L"\nCRC32 test1 FAILED");
if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE)
mprintf(L"\nCRC32 test2 OK");
else
mprintf(L"\nCRC32 test2 FAILED");
for (uint I=0;I<14;I++) // Check for possible int sign extension.
b[I]=(byte)0x7f+I;
if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA)
mprintf(L"\nCRC32 test3 OK");
else
mprintf(L"\nCRC32 test3 FAILED");
for (uint I=0;I<FirstSize;I++)
b[I]=(byte)I;
uint r32=CRC32(0xffffffff,b,FirstSize);
for (uint I=FirstSize;I<1024;I++)
{
b[0]=(byte)I;
r32=CRC32(r32,b,1);
}
if ((r32^0xffffffff)==0xB70B4C26)
mprintf(L"\nCRC32 test4 OK");
else
mprintf(L"\nCRC32 test4 FAILED");
if ((CRC64(0xffffffffffffffff,(byte*)"testtesttest",12)^0xffffffffffffffff)==0x7B1C2D230EDEB436)
mprintf(L"\nCRC64 test1 OK");
else
mprintf(L"\nCRC64 test1 FAILED");
if (CRC64(0,(byte*)"te\x80st",5)==0xB5DBF9583A6EED4A)
mprintf(L"\nCRC64 test2 OK");
else
mprintf(L"\nCRC64 test2 FAILED");
for (uint I=0;I<14;I++) // Check for possible int sign extension.
b[I]=(byte)0x7f+I;
if ((CRC64(0xffffffffffffffff,b,14)^0xffffffffffffffff)==0xE019941C05B2820C)
mprintf(L"\nCRC64 test3 OK");
else
mprintf(L"\nCRC64 test3 FAILED");
for (uint I=0;I<FirstSize;I++)
b[I]=(byte)I;
uint64 r64=CRC64(0xffffffffffffffff,b,FirstSize);
for (uint I=FirstSize;I<1024;I++)
{
b[0]=(byte)I;
r64=CRC64(r64,b,1);
}
if ((r64^0xffffffffffffffff)==0xD51FB58DC789C400)
mprintf(L"\nCRC64 test4 OK");
else
mprintf(L"\nCRC64 test4 FAILED");
const size_t BufSize=0x100000;
byte *Buf=new byte[BufSize];
GetRnd(Buf,BufSize);
clock_t StartTime=clock();
r32=0xffffffff;
const uint64 BufCount=5000;
for (uint I=0;I<BufCount;I++)
r32=CRC32(r32,Buf,BufSize);
if (r32!=0) // Otherwise compiler optimizer removes CRC calculation.
mprintf(L"\nCRC32 speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
StartTime=clock();
DataHash Hash;
Hash.Init(HASH_CRC32,MaxPoolThreads);
const uint64 BufCountMT=20000;
for (uint I=0;I<BufCountMT;I++)
Hash.Update(Buf,BufSize);
HashValue Result;
Hash.Result(&Result);
mprintf(L"\nCRC32 MT speed: %llu MB/s",BufCountMT*CLOCKS_PER_SEC/(clock()-StartTime));
StartTime=clock();
Hash.Init(HASH_BLAKE2,MaxPoolThreads);
for (uint I=0;I<BufCount;I++)
Hash.Update(Buf,BufSize);
Hash.Result(&Result);
mprintf(L"\nBlake2sp speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
StartTime=clock();
r64=0xffffffffffffffff;
for (uint I=0;I<BufCount;I++)
r64=CRC64(r64,Buf,BufSize);
if (r64!=0) // Otherwise compiler optimizer removes CRC calculation.
mprintf(L"\nCRC64 speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
}
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef _RAR_CRC_
#define _RAR_CRC_
// This function is only to intialize external CRC tables. We do not need to
// call it before calculating CRC32.
void InitCRC32(uint *CRCTab);
uint CRC32(uint StartCRC,const void *Addr,size_t Size);
#ifndef SFX_MODULE
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
#endif
#endif
+134
View File
@@ -0,0 +1,134 @@
#include "rar.hpp"
#ifndef SFX_MODULE
#include "crypt1.cpp"
#include "crypt2.cpp"
#endif
#include "crypt3.cpp"
#include "crypt5.cpp"
CryptData::CryptData()
{
Method=CRYPT_NONE;
KDF3CachePos=0;
KDF5CachePos=0;
memset(CRCTab,0,sizeof(CRCTab));
}
void CryptData::DecryptBlock(byte *Buf,size_t Size)
{
switch(Method)
{
#ifndef SFX_MODULE
case CRYPT_RAR13:
Decrypt13(Buf,Size);
break;
case CRYPT_RAR15:
Crypt15(Buf,Size);
break;
case CRYPT_RAR20:
for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE)
DecryptBlock20(Buf+I);
break;
#endif
case CRYPT_RAR30:
case CRYPT_RAR50:
rin.blockDecrypt(Buf,Size,Buf);
break;
}
}
bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
if (Method==CRYPT_NONE || !Password->IsSet())
return false;
CryptData::Method=Method;
wchar PwdW[MAXPASSWORD];
Password->Get(PwdW,ASIZE(PwdW));
if (wcslen(PwdW)>=MAXPASSWORD_RAR)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD_RAR-1);
PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
char PwdA[MAXPASSWORD];
WideToChar(PwdW,PwdA,ASIZE(PwdA));
PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
bool Success=true;
switch(Method)
{
#ifndef SFX_MODULE
case CRYPT_RAR13:
SetKey13(PwdA);
break;
case CRYPT_RAR15:
SetKey15(PwdA);
break;
case CRYPT_RAR20:
SetKey20(PwdA);
break;
#endif
case CRYPT_RAR30:
SetKey30(Encrypt,Password,PwdW,Salt);
break;
case CRYPT_RAR50:
Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
break;
}
cleandata(PwdA,sizeof(PwdA));
cleandata(PwdW,sizeof(PwdW));
return Success;
}
// Use the current system time to additionally randomize data.
static void TimeRandomize(byte *RndBuf,size_t BufSize)
{
static uint Count=0;
RarTime CurTime;
CurTime.SetCurrentTime();
uint64 Random=CurTime.GetWin()+clock();
for (size_t I=0;I<BufSize;I++)
{
byte RndByte = byte (Random >> ( (I & 7) * 8 ));
RndBuf[I]=byte( (RndByte ^ I) + Count++);
}
}
// Fill buffer with random data.
void GetRnd(byte *RndBuf,size_t BufSize)
{
bool Success=false;
#if defined(_WIN_ALL)
HCRYPTPROV hProvider = 0;
if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) != FALSE;
CryptReleaseContext(hProvider, 0);
}
#elif defined(_UNIX)
FILE *rndf = fopen("/dev/urandom", "r");
if (rndf!=NULL)
{
Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
fclose(rndf);
}
#endif
// We use this code only as the last resort if code above failed.
if (!Success)
TimeRandomize(RndBuf,BufSize);
}
+171
View File
@@ -0,0 +1,171 @@
#ifndef _RAR_CRYPT_
#define _RAR_CRYPT_
enum CRYPT_METHOD {
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50,
CRYPT_UNKNOWN
};
#define SIZE_SALT50 16
#define SIZE_SALT30 8
#define SIZE_INITV 16
#define SIZE_PSWCHECK 8
#define SIZE_PSWCHECK_CSUM 4
#define CRYPT_BLOCK_SIZE 16
#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
#define CRYPT_VERSION 0 // Supported encryption version.
class CryptData
{
struct KDF5CacheItem
{
SecPassword Pwd;
byte Salt[SIZE_SALT50];
byte Key[32];
uint Lg2Count; // Log2 of PBKDF2 repetition count.
byte PswCheckValue[SHA256_DIGEST_SIZE];
byte HashKeyValue[SHA256_DIGEST_SIZE];
KDF5CacheItem() {Clean();}
~KDF5CacheItem() {Clean();}
void Clean()
{
cleandata(Salt,sizeof(Salt));
cleandata(Key,sizeof(Key));
cleandata(&Lg2Count,sizeof(Lg2Count));
cleandata(PswCheckValue,sizeof(PswCheckValue));
cleandata(HashKeyValue,sizeof(HashKeyValue));
}
};
struct KDF3CacheItem
{
SecPassword Pwd;
byte Salt[SIZE_SALT30];
byte Key[16];
byte Init[16];
bool SaltPresent;
KDF3CacheItem() {Clean();}
~KDF3CacheItem() {Clean();}
void Clean()
{
cleandata(Salt,sizeof(Salt));
cleandata(Key,sizeof(Key));
cleandata(Init,sizeof(Init));
cleandata(&SaltPresent,sizeof(SaltPresent));
}
};
private:
void SetKey13(const char *Password);
void Decrypt13(byte *Data,size_t Count);
void SetKey15(const char *Password);
void Crypt15(byte *Data,size_t Count);
void SetKey20(const char *Password);
void Swap20(byte *Ch1,byte *Ch2);
void UpdKeys20(byte *Buf);
void EncryptBlock20(byte *Buf);
void DecryptBlock20(byte *Buf);
void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
bool SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
KDF3CacheItem KDF3Cache[4];
uint KDF3CachePos;
KDF5CacheItem KDF5Cache[4];
uint KDF5CachePos;
CRYPT_METHOD Method;
Rijndael rin;
uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
byte SubstTable20[256];
uint Key20[4];
byte Key13[3];
ushort Key15[4];
public:
CryptData();
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,
byte *HashKey,byte *PswCheck);
void SetCmt13Encryption();
void EncryptBlock(byte *Buf,size_t Size);
void DecryptBlock(byte *Buf,size_t Size);
static void SetSalt(byte *Salt,size_t SaltSize);
};
class CheckPassword
{
public:
enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
virtual CONFIDENCE GetConfidence()=0;
virtual bool Check(SecPassword *Password)=0;
};
class RarCheckPassword:public CheckPassword
{
private:
CryptData *Crypt;
uint Lg2Count;
byte Salt[SIZE_SALT50];
byte InitV[SIZE_INITV];
byte PswCheck[SIZE_PSWCHECK];
public:
RarCheckPassword()
{
Crypt=NULL;
}
~RarCheckPassword()
{
delete Crypt;
}
void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
{
if (Crypt==NULL)
Crypt=new CryptData;
memcpy(this->Salt,Salt,sizeof(this->Salt));
memcpy(this->InitV,InitV,sizeof(this->InitV));
this->Lg2Count=Lg2Count;
memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
}
bool IsSet() {return Crypt!=NULL;}
// RAR5 provides the higly reliable 64 bit password verification value.
CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
bool Check(SecPassword *Password)
{
byte PswCheck[SIZE_PSWCHECK];
Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
}
};
void GetRnd(byte *RndBuf,size_t BufSize);
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
size_t DataLength,byte *ResDigest);
void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
size_t salt_len,byte *key, byte *Value1, byte *Value2,
uint rounds);
void ConvertHashToMAC(HashValue *Value,byte *Key);
#endif
+66
View File
@@ -0,0 +1,66 @@
void CryptData::SetKey13(const char *Password)
{
Key13[0]=Key13[1]=Key13[2]=0;
for (size_t I=0;Password[I]!=0;I++)
{
byte P=Password[I];
Key13[0]+=P;
Key13[1]^=P;
Key13[2]+=P;
Key13[2]=(byte)rotls(Key13[2],1,8);
}
}
void CryptData::SetKey15(const char *Password)
{
InitCRC32(CRCTab);
uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
Key15[0]=PswCRC&0xffff;
Key15[1]=(PswCRC>>16)&0xffff;
Key15[2]=Key15[3]=0;
for (size_t I=0;Password[I]!=0;I++)
{
byte P=Password[I];
Key15[2]^=P^CRCTab[P];
Key15[3]+=ushort(P+(CRCTab[P]>>16));
}
}
void CryptData::SetCmt13Encryption()
{
Method=CRYPT_RAR13;
Key13[0]=0;
Key13[1]=7;
Key13[2]=77;
}
void CryptData::Decrypt13(byte *Data,size_t Count)
{
while (Count--)
{
Key13[1]+=Key13[2];
Key13[0]+=Key13[1];
*Data-=Key13[0];
Data++;
}
}
void CryptData::Crypt15(byte *Data,size_t Count)
{
while (Count--)
{
Key15[0]+=0x1234;
Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
Key15[2]-=ushort(CRCTab[(Key15[0] & 0x1fe)>>1]>>16);
Key15[0]^=Key15[2];
Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
Key15[3]=rotrs(Key15[3]&0xffff,1,16);
Key15[0]^=Key15[3];
*Data^=(byte)(Key15[0]>>8);
Data++;
}
}
+133
View File
@@ -0,0 +1,133 @@
#define NROUNDS 32
#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
((uint)SubstTable20[(int)(t>>24)&255]<<24) )
static byte InitSubstTable20[256]={
215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
};
void CryptData::SetKey20(const char *Password)
{
InitCRC32(CRCTab);
char Psw[MAXPASSWORD];
strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
size_t PswLength=strlen(Psw);
Key20[0]=0xD3A3B879L;
Key20[1]=0x3F6D12F7L;
Key20[2]=0x7515A235L;
Key20[3]=0xA4E7F123L;
memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
for (uint J=0;J<256;J++)
for (size_t I=0;I<PswLength;I+=2)
{
uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff];
uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff];
for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++)
Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]);
}
// Incomplete last block of password must be zero padded.
if ((PswLength & CRYPT_BLOCK_MASK)!=0)
for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++)
Psw[I]=0;
for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE)
EncryptBlock20((byte *)Psw+I);
}
void CryptData::EncryptBlock20(byte *Buf)
{
uint A,B,C,D,T,TA,TB;
A=RawGet4(Buf+0)^Key20[0];
B=RawGet4(Buf+4)^Key20[1];
C=RawGet4(Buf+8)^Key20[2];
D=RawGet4(Buf+12)^Key20[3];
for(int I=0;I<NROUNDS;I++)
{
T=((C+rotls(D,11,32))^Key20[I&3]);
TA=A^substLong(T);
T=((D^rotls(C,17,32))+Key20[I&3]);
TB=B^substLong(T);
A=C;
B=D;
C=TA;
D=TB;
}
RawPut4(C^Key20[0],Buf+0);
RawPut4(D^Key20[1],Buf+4);
RawPut4(A^Key20[2],Buf+8);
RawPut4(B^Key20[3],Buf+12);
UpdKeys20(Buf);
}
void CryptData::DecryptBlock20(byte *Buf)
{
byte InBuf[16];
uint A,B,C,D,T,TA,TB;
A=RawGet4(Buf+0)^Key20[0];
B=RawGet4(Buf+4)^Key20[1];
C=RawGet4(Buf+8)^Key20[2];
D=RawGet4(Buf+12)^Key20[3];
memcpy(InBuf,Buf,sizeof(InBuf));
for(int I=NROUNDS-1;I>=0;I--)
{
T=((C+rotls(D,11,32))^Key20[I&3]);
TA=A^substLong(T);
T=((D^rotls(C,17,32))+Key20[I&3]);
TB=B^substLong(T);
A=C;
B=D;
C=TA;
D=TB;
}
RawPut4(C^Key20[0],Buf+0);
RawPut4(D^Key20[1],Buf+4);
RawPut4(A^Key20[2],Buf+8);
RawPut4(B^Key20[3],Buf+12);
UpdKeys20(InBuf);
}
void CryptData::UpdKeys20(byte *Buf)
{
for (int I=0;I<16;I+=4)
{
Key20[0]^=CRCTab[Buf[I]];
Key20[1]^=CRCTab[Buf[I+1]];
Key20[2]^=CRCTab[Buf[I+2]];
Key20[3]^=CRCTab[Buf[I+3]];
}
}
void CryptData::Swap20(byte *Ch1,byte *Ch2)
{
byte Ch=*Ch1;
*Ch1=*Ch2;
*Ch2=Ch;
}
+69
View File
@@ -0,0 +1,69 @@
void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
{
byte AESKey[16],AESInit[16];
bool Cached=false;
for (uint I=0;I<ASIZE(KDF3Cache);I++)
if (KDF3Cache[I].Pwd==*Password &&
(Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL &&
KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0))
{
memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey));
SecHideData(AESKey,sizeof(AESKey),false,false);
memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit));
Cached=true;
break;
}
if (!Cached)
{
byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
size_t PswLength=wcslen(PwdW);
size_t RawLength=2*PswLength;
WideToRaw(PwdW,PswLength,RawPsw,RawLength);
if (Salt!=NULL)
{
memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);
RawLength+=SIZE_SALT30;
}
sha1_context c;
sha1_init(&c);
const uint HashRounds=0x40000;
for (uint I=0;I<HashRounds;I++)
{
sha1_process_rar29( &c, RawPsw, RawLength );
byte PswNum[3];
PswNum[0]=(byte)I;
PswNum[1]=(byte)(I>>8);
PswNum[2]=(byte)(I>>16);
sha1_process(&c, PswNum, 3);
if (I%(HashRounds/16)==0)
{
sha1_context tempc=c;
uint32 digest[5];
sha1_done( &tempc, digest );
AESInit[I/(HashRounds/16)]=(byte)digest[4];
}
}
uint32 digest[5];
sha1_done( &c, digest );
for (uint I=0;I<4;I++)
for (uint J=0;J<4;J++)
AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
KDF3Cache[KDF3CachePos].Pwd=*Password;
if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false);
memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
cleandata(RawPsw,sizeof(RawPsw));
}
rin.Init(Encrypt, AESKey, 128, AESInit);
cleandata(AESKey,sizeof(AESKey));
cleandata(AESInit,sizeof(AESInit));
}
+235
View File
@@ -0,0 +1,235 @@
static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
size_t DataLength,byte *ResDigest,
sha256_context *ICtxOpt,bool *SetIOpt,
sha256_context *RCtxOpt,bool *SetROpt)
{
const size_t Sha256BlockSize=64; // As defined in RFC 4868.
byte KeyHash[SHA256_DIGEST_SIZE];
if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
{
sha256_context KCtx;
sha256_init(&KCtx);
sha256_process(&KCtx, Key, KeyLength);
sha256_done(&KCtx, KeyHash);
Key = KeyHash;
KeyLength = SHA256_DIGEST_SIZE;
}
byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
sha256_context ICtx;
if (ICtxOpt!=NULL && *SetIOpt)
ICtx=*ICtxOpt; // Use already calculated the first block context.
else
{
// This calculation is the same for all iterations with same password.
// So for PBKDF2 we can calculate it only for first block and then reuse
// to improve performance.
for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
KeyBuf[I] = Key[I] ^ 0x36;
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
KeyBuf[I] = 0x36;
sha256_init(&ICtx);
sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
}
if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse.
{
*ICtxOpt=ICtx;
*SetIOpt=true;
}
sha256_process(&ICtx, Data, DataLength); // Hash data.
byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
sha256_done(&ICtx, IDig);
sha256_context RCtx;
if (RCtxOpt!=NULL && *SetROpt)
RCtx=*RCtxOpt; // Use already calculated first block context.
else
{
// This calculation is the same for all iterations with same password.
// So for PBKDF2 we can calculate it only for first block and then reuse
// to improve performance.
for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
KeyBuf[I] = Key[I] ^ 0x5c;
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
KeyBuf[I] = 0x5c;
sha256_init(&RCtx);
sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
}
if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse.
{
*RCtxOpt=RCtx;
*SetROpt=true;
}
sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
sha256_done(&RCtx, ResDigest);
}
// PBKDF2 for 32 byte key length. We generate the key for specified number
// of iteration count also as two supplementary values (key for checksums
// and password verification) for iterations+16 and iterations+32.
void pbkdf2(const byte *Pwd, size_t PwdLength,
const byte *Salt, size_t SaltLength,
byte *Key, byte *V1, byte *V2, uint Count)
{
const size_t MaxSalt=64;
byte SaltData[MaxSalt+4];
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
SaltData[SaltLength + 0] = 0; // Block index appened to salt.
SaltData[SaltLength + 1] = 0; //
SaltData[SaltLength + 2] = 0; // Since we do not request the key width
SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
// First iteration: HMAC of password, salt and block index (1).
byte U1[SHA256_DIGEST_SIZE];
hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL);
byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
uint CurCount[] = { Count-1, 16, 16 };
byte *CurValue[] = { Key , V1, V2 };
sha256_context ICtxOpt,RCtxOpt;
bool SetIOpt=false,SetROpt=false;
byte U2[SHA256_DIGEST_SIZE];
for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
{
for (uint J = 0; J < CurCount[I]; J++)
{
// U2 = PRF (P, U1).
hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt);
memcpy(U1, U2, sizeof(U1));
for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
Fn[K] ^= U1[K];
}
memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
}
cleandata(SaltData, sizeof(SaltData));
cleandata(Fn, sizeof(Fn));
cleandata(U1, sizeof(U1));
cleandata(U2, sizeof(U2));
}
bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
byte *PswCheck)
{
if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
return false;
byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
bool Found=false;
for (uint I=0;I<ASIZE(KDF5Cache);I++)
{
KDF5CacheItem *Item=KDF5Cache+I;
if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{
memcpy(Key,Item->Key,sizeof(Key));
SecHideData(Key,sizeof(Key),false,false);
memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
Found=true;
break;
}
}
if (!Found)
{
char PwdUtf[MAXPASSWORD*4];
WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt));
cleandata(PwdUtf,sizeof(PwdUtf));
KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache));
Item->Lg2Count=Lg2Cnt;
Item->Pwd=*Password;
memcpy(Item->Salt,Salt,SIZE_SALT50);
memcpy(Item->Key,Key,sizeof(Item->Key));
memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
SecHideData(Item->Key,sizeof(Item->Key),true,false);
}
if (HashKey!=NULL)
memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
if (PswCheck!=NULL)
{
memset(PswCheck,0,SIZE_PSWCHECK);
for (uint I=0;I<SHA256_DIGEST_SIZE;I++)
PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I];
cleandata(PswCheckValue,sizeof(PswCheckValue));
}
// NULL initialization vector is possible if we only need the password
// check value for archive encryption header.
if (InitV!=NULL)
rin.Init(Encrypt, Key, 256, InitV);
cleandata(Key,sizeof(Key));
return true;
}
void ConvertHashToMAC(HashValue *Value,byte *Key)
{
if (Value->Type==HASH_CRC32)
{
byte RawCRC[4];
RawPut4(Value->CRC32,RawCRC);
byte Digest[SHA256_DIGEST_SIZE];
hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL);
Value->CRC32=0;
for (uint I=0;I<ASIZE(Digest);I++)
Value->CRC32^=Digest[I] << ((I & 3) * 8);
Value->CRC32&=0xffffffff; // In case the variable size is larger than 32-bit.
}
if (Value->Type==HASH_BLAKE2)
{
byte Digest[BLAKE2_DIGEST_SIZE];
hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL);
memcpy(Value->Digest,Digest,sizeof(Value->Digest));
}
}
#if 0
static void TestPBKDF2();
struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
{
byte Key[32],V1[32],V2[32];
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
}
#endif
+513
View File
@@ -0,0 +1,513 @@
#include "rar.hpp"
static int RarErrorToDll(RAR_EXIT ErrCode);
struct DataSet
{
CommandData Cmd;
Archive Arc;
CmdExtract Extract;
int OpenMode;
int HeaderSize;
DataSet():Arc(&Cmd),Extract(&Cmd) {};
};
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
{
RAROpenArchiveDataEx rx{};
rx.ArcName=r->ArcName;
rx.OpenMode=r->OpenMode;
rx.CmtBuf=r->CmtBuf;
rx.CmtBufSize=r->CmtBufSize;
HANDLE hArc=RAROpenArchiveEx(&rx);
r->OpenResult=rx.OpenResult;
r->CmtSize=rx.CmtSize;
r->CmtState=rx.CmtState;
return hArc;
}
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
DataSet *Data=nullptr;
try
{
ErrHandler.Clean();
r->OpenResult=0;
Data=new DataSet;
Data->Cmd.DllError=0;
Data->OpenMode=r->OpenMode;
Data->Cmd.FileArgs.AddString(L"*");
Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
std::string AnsiArcName;
if (r->ArcName!=nullptr)
{
AnsiArcName=r->ArcName;
#ifdef _WIN_ALL
if (!AreFileApisANSI())
OemToExt(r->ArcName,AnsiArcName);
#endif
}
std::wstring ArcName;
if (r->ArcNameW!=nullptr && *r->ArcNameW!=0)
ArcName=r->ArcNameW;
else
CharToWide(AnsiArcName,ArcName);
Data->Cmd.AddArcName(ArcName);
Data->Cmd.Overwrite=OVERWRITE_ALL;
Data->Cmd.VersionControl=1;
Data->Cmd.Callback=r->Callback;
Data->Cmd.UserData=r->UserData;
// Open shared mode is added by request of dll users, who need to
// browse and unpack archives while downloading.
Data->Cmd.OpenShared = true;
if (!Data->Arc.Open(ArcName,FMF_OPENSHARED))
{
r->OpenResult=ERAR_EOPEN;
delete Data;
return nullptr;
}
if (!Data->Arc.IsArchive(true))
{
if (Data->Cmd.DllError!=0)
r->OpenResult=Data->Cmd.DllError;
else
{
RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING)
r->OpenResult=RarErrorToDll(ErrCode);
else
r->OpenResult=ERAR_BAD_ARCHIVE;
}
delete Data;
return nullptr;
}
r->Flags=0;
if (Data->Arc.Volume)
r->Flags|=ROADF_VOLUME;
if (Data->Arc.MainComment)
r->Flags|=ROADF_COMMENT;
if (Data->Arc.Locked)
r->Flags|=ROADF_LOCK;
if (Data->Arc.Solid)
r->Flags|=ROADF_SOLID;
if (Data->Arc.NewNumbering)
r->Flags|=ROADF_NEWNUMBERING;
if (Data->Arc.Signed)
r->Flags|=ROADF_SIGNED;
if (Data->Arc.Protected)
r->Flags|=ROADF_RECOVERY;
if (Data->Arc.Encrypted)
r->Flags|=ROADF_ENCHEADERS;
if (Data->Arc.FirstVolume)
r->Flags|=ROADF_FIRSTVOLUME;
std::wstring CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW))
{
if (r->CmtBufW!=nullptr)
{
// CmtDataW.push_back(0);
size_t Size=wcslen(CmtDataW.data())+1;
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
memcpy(r->CmtBufW,CmtDataW.data(),(r->CmtSize-1)*sizeof(*r->CmtBufW));
r->CmtBufW[r->CmtSize-1]=0;
}
else
if (r->CmtBuf!=NULL)
{
std::vector<char> CmtData(CmtDataW.size()*4+1);
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.size()-1);
size_t Size=strlen(CmtData.data())+1;
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
memcpy(r->CmtBuf,CmtData.data(),r->CmtSize-1);
r->CmtBuf[r->CmtSize-1]=0;
}
}
else
r->CmtState=r->CmtSize=0;
#ifdef PROPAGATE_MOTW
if (r->MarkOfTheWeb!=nullptr)
{
Data->Cmd.MotwAllFields=r->MarkOfTheWeb[0]=='1';
const wchar *Sep=wcschr(r->MarkOfTheWeb,'=');
if (r->MarkOfTheWeb[0]=='-')
Data->Cmd.MotwList.Reset();
else
Data->Cmd.GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,Data->Cmd.MotwList);
}
#endif
Data->Extract.ExtractArchiveInit(Data->Arc);
return (HANDLE)Data;
}
catch (RAR_EXIT ErrCode)
{
if (Data!=NULL && Data->Cmd.DllError!=0)
r->OpenResult=Data->Cmd.DllError;
else
r->OpenResult=RarErrorToDll(ErrCode);
if (Data != NULL)
delete Data;
return NULL;
}
catch (std::bad_alloc&) // Catch 'new' exception.
{
r->OpenResult=ERAR_NO_MEMORY;
if (Data != NULL)
delete Data;
}
return NULL; // To make compilers happy.
}
int PASCAL RARCloseArchive(HANDLE hArcData)
{
DataSet *Data=(DataSet *)hArcData;
try
{
bool Success=Data==NULL ? false:Data->Arc.Close();
delete Data;
return Success ? ERAR_SUCCESS : ERAR_ECLOSE;
}
catch (RAR_EXIT ErrCode)
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
}
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
{
struct RARHeaderDataEx X{};
int Code=RARReadHeaderEx(hArcData,&X);
strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName));
strncpyz(D->FileName,X.FileName,ASIZE(D->FileName));
D->Flags=X.Flags;
D->PackSize=X.PackSize;
D->UnpSize=X.UnpSize;
D->HostOS=X.HostOS;
D->FileCRC=X.FileCRC;
D->FileTime=X.FileTime;
D->UnpVer=X.UnpVer;
D->Method=X.Method;
D->FileAttr=X.FileAttr;
D->CmtSize=0;
D->CmtState=0;
return Code;
}
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
{
DataSet *Data=(DataSet *)hArcData;
try
{
if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
{
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
Data->Arc.EndArcHead.NextVolume)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
return RARReadHeaderEx(hArcData,D);
}
else
return ERAR_EOPEN;
if (Data->Arc.BrokenHeader)
return ERAR_BAD_DATA;
// Might be necessary if RARSetPassword is still called instead of
// open callback for RAR5 archives and if password is invalid.
if (Data->Arc.FailedHeaderDecryption)
return ERAR_BAD_PASSWORD;
return ERAR_END_ARCHIVE;
}
FileHeader *hd=&Data->Arc.FileHead;
if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore)
{
int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
if (Code==0)
return RARReadHeaderEx(hArcData,D);
else
return Code;
}
wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW));
WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
if (D->ArcNameEx!=nullptr)
wcsncpyz(D->ArcNameEx,Data->Arc.FileName.c_str(),D->ArcNameExSize);
wcsncpyz(D->FileNameW,hd->FileName.c_str(),ASIZE(D->FileNameW));
WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
#ifdef _WIN_ALL
CharToOemA(D->FileName,D->FileName);
#endif
if (D->FileNameEx!=nullptr)
wcsncpyz(D->FileNameEx,hd->FileName.c_str(),D->FileNameExSize);
D->Flags=0;
if (hd->SplitBefore)
D->Flags|=RHDF_SPLITBEFORE;
if (hd->SplitAfter)
D->Flags|=RHDF_SPLITAFTER;
if (hd->Encrypted)
D->Flags|=RHDF_ENCRYPTED;
if (hd->Solid)
D->Flags|=RHDF_SOLID;
if (hd->Dir)
D->Flags|=RHDF_DIRECTORY;
D->PackSize=uint(hd->PackSize & 0xffffffff);
D->PackSizeHigh=uint(hd->PackSize>>32);
D->UnpSize=uint(hd->UnpSize & 0xffffffff);
D->UnpSizeHigh=uint(hd->UnpSize>>32);
D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
D->UnpVer=Data->Arc.FileHead.UnpVer;
D->FileCRC=hd->FileHash.CRC32;
D->FileTime=hd->mtime.GetDos();
uint64 MRaw=hd->mtime.GetWin();
D->MtimeLow=(uint)MRaw;
D->MtimeHigh=(uint)(MRaw>>32);
uint64 CRaw=hd->ctime.GetWin();
D->CtimeLow=(uint)CRaw;
D->CtimeHigh=(uint)(CRaw>>32);
uint64 ARaw=hd->atime.GetWin();
D->AtimeLow=(uint)ARaw;
D->AtimeHigh=(uint)(ARaw>>32);
D->Method=hd->Method+0x30;
D->FileAttr=hd->FileAttr;
D->CmtSize=0;
D->CmtState=0;
D->DictSize=uint(hd->WinSize/1024);
switch (hd->FileHash.Type)
{
case HASH_RAR14:
case HASH_CRC32:
D->HashType=RAR_HASH_CRC32;
break;
case HASH_BLAKE2:
D->HashType=RAR_HASH_BLAKE2;
memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
break;
default:
D->HashType=RAR_HASH_NONE;
break;
}
D->RedirType=hd->RedirType;
// RedirNameSize sanity check is useful in case some developer
// did not initialize Reserved area with 0 as required in docs.
// We have taken 'Redir*' fields from Reserved area. We may remove
// this RedirNameSize check sometimes later.
if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
D->RedirNameSize>0 && D->RedirNameSize<100000)
wcsncpyz(D->RedirName,hd->RedirName.c_str(),D->RedirNameSize);
D->DirTarget=hd->DirTarget;
}
catch (RAR_EXIT ErrCode)
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
}
int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW)
{
DataSet *Data=(DataSet *)hArcData;
try
{
Data->Cmd.DllError=0;
if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
Operation==RAR_SKIP && !Data->Arc.Solid)
{
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
Data->Arc.FileHead.SplitAfter)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
return ERAR_SUCCESS;
}
else
return ERAR_EOPEN;
Data->Arc.SeekToNext();
}
else
{
Data->Cmd.DllOpMode=Operation;
Data->Cmd.ExtrPath.clear();
Data->Cmd.DllDestName.clear();
if (DestPath!=NULL)
{
std::string ExtrPathA=DestPath;
#ifdef _WIN_ALL
// We must not apply OemToCharBuffA directly to DestPath,
// because we do not know DestPath length and OemToCharBuffA
// does not stop at 0.
OemToExt(ExtrPathA,ExtrPathA);
#endif
CharToWide(ExtrPathA,Data->Cmd.ExtrPath);
AddEndSlash(Data->Cmd.ExtrPath);
}
if (DestName!=NULL)
{
std::string DestNameA=DestName;
#ifdef _WIN_ALL
// We must not apply OemToCharBuffA directly to DestName,
// because we do not know DestName length and OemToCharBuffA
// does not stop at 0.
OemToExt(DestNameA,DestNameA);
#endif
CharToWide(DestNameA,Data->Cmd.DllDestName);
}
if (DestPathW!=NULL)
{
Data->Cmd.ExtrPath=DestPathW;
AddEndSlash(Data->Cmd.ExtrPath);
}
if (DestNameW!=NULL)
Data->Cmd.DllDestName=DestNameW;
Data->Cmd.Command=Operation==RAR_EXTRACT ? L"X":L"T";
Data->Cmd.Test=Operation!=RAR_EXTRACT;
bool Repeat=false;
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
// Now we process extra file information if any.
// It is important to do it in the same ProcessFile(), because caller
// app can rely on this behavior, for example, to overwrite
// the extracted Mark of the Web with propagated from archive
// immediately after ProcessFile() call.
//
// Archive can be closed if we process volumes, next volume is missing
// and current one is already removed or deleted. So we need to check
// if archive is still open to avoid calling file operations on
// the invalid file handle. Some of our file operations like Seek()
// process such invalid handle correctly, some not.
while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
Data->Arc.GetHeaderType()==HEAD_SERVICE)
{
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
Data->Arc.SeekToNext();
}
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
}
}
catch (std::bad_alloc&)
{
return ERAR_NO_MEMORY;
}
catch (RAR_EXIT ErrCode)
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
}
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
{
return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL);
}
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
{
return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName);
}
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc)
{
DataSet *Data=(DataSet *)hArcData;
Data->Cmd.ChangeVolProc=ChangeVolProc;
}
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData)
{
DataSet *Data=(DataSet *)hArcData;
Data->Cmd.Callback=Callback;
Data->Cmd.UserData=UserData;
}
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc)
{
DataSet *Data=(DataSet *)hArcData;
Data->Cmd.ProcessDataProc=ProcessDataProc;
}
void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
{
#ifndef RAR_NOCRYPT
DataSet *Data=(DataSet *)hArcData;
wchar PasswordW[MAXPASSWORD];
CharToWide(Password,PasswordW,ASIZE(PasswordW));
Data->Cmd.Password.Set(PasswordW);
cleandata(PasswordW,sizeof(PasswordW));
#endif
}
int PASCAL RARGetDllVersion()
{
return RAR_DLL_VERSION;
}
static int RarErrorToDll(RAR_EXIT ErrCode)
{
switch(ErrCode)
{
case RARX_FATAL:
case RARX_READ:
return ERAR_EREAD;
case RARX_CRC:
return ERAR_BAD_DATA;
case RARX_WRITE:
return ERAR_EWRITE;
case RARX_OPEN:
return ERAR_EOPEN;
case RARX_CREATE:
return ERAR_ECREATE;
case RARX_MEMORY:
return ERAR_NO_MEMORY;
case RARX_BADPWD:
return ERAR_BAD_PASSWORD;
case RARX_SUCCESS:
return ERAR_SUCCESS; // 0.
case RARX_BADARC:
return ERAR_BAD_ARCHIVE;
default:
return ERAR_UNKNOWN;
}
}
+13
View File
@@ -0,0 +1,13 @@
EXPORTS
RAROpenArchive
RAROpenArchiveEx
RARCloseArchive
RARReadHeader
RARReadHeaderEx
RARProcessFile
RARProcessFileW
RARSetCallback
RARSetChangeVolProc
RARSetProcessDataProc
RARSetPassword
RARGetDllVersion
+195
View File
@@ -0,0 +1,195 @@
#ifndef _UNRAR_DLL_
#define _UNRAR_DLL_
#pragma pack(push, 1)
#define ERAR_SUCCESS 0
#define ERAR_END_ARCHIVE 10
#define ERAR_NO_MEMORY 11
#define ERAR_BAD_DATA 12
#define ERAR_BAD_ARCHIVE 13
#define ERAR_UNKNOWN_FORMAT 14
#define ERAR_EOPEN 15
#define ERAR_ECREATE 16
#define ERAR_ECLOSE 17
#define ERAR_EREAD 18
#define ERAR_EWRITE 19
#define ERAR_SMALL_BUF 20
#define ERAR_UNKNOWN 21
#define ERAR_MISSING_PASSWORD 22
#define ERAR_EREFERENCE 23
#define ERAR_BAD_PASSWORD 24
#define ERAR_LARGE_DICT 25
#define RAR_OM_LIST 0
#define RAR_OM_EXTRACT 1
#define RAR_OM_LIST_INCSPLIT 2
#define RAR_SKIP 0
#define RAR_TEST 1
#define RAR_EXTRACT 2
#define RAR_VOL_ASK 0
#define RAR_VOL_NOTIFY 1
#define RAR_DLL_VERSION 9
#define RAR_HASH_NONE 0
#define RAR_HASH_CRC32 1
#define RAR_HASH_BLAKE2 2
#ifdef _UNIX
#define CALLBACK
#define PASCAL
#define LONG long
#define HANDLE void *
#define LPARAM long
#define UINT unsigned int
#endif
#define RHDF_SPLITBEFORE 0x01
#define RHDF_SPLITAFTER 0x02
#define RHDF_ENCRYPTED 0x04
#define RHDF_SOLID 0x10
#define RHDF_DIRECTORY 0x20
struct RARHeaderData
{
char ArcName[260];
char FileName[260];
unsigned int Flags;
unsigned int PackSize;
unsigned int UnpSize;
unsigned int HostOS;
unsigned int FileCRC;
unsigned int FileTime;
unsigned int UnpVer;
unsigned int Method;
unsigned int FileAttr;
char *CmtBuf;
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
};
struct RARHeaderDataEx
{
char ArcName[1024];
wchar_t ArcNameW[1024];
char FileName[1024];
wchar_t FileNameW[1024];
unsigned int Flags;
unsigned int PackSize;
unsigned int PackSizeHigh;
unsigned int UnpSize;
unsigned int UnpSizeHigh;
unsigned int HostOS;
unsigned int FileCRC;
unsigned int FileTime;
unsigned int UnpVer;
unsigned int Method;
unsigned int FileAttr;
char *CmtBuf;
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
unsigned int DictSize;
unsigned int HashType;
char Hash[32];
unsigned int RedirType;
wchar_t *RedirName;
unsigned int RedirNameSize;
unsigned int DirTarget;
unsigned int MtimeLow;
unsigned int MtimeHigh;
unsigned int CtimeLow;
unsigned int CtimeHigh;
unsigned int AtimeLow;
unsigned int AtimeHigh;
wchar_t *ArcNameEx;
unsigned int ArcNameExSize;
wchar_t *FileNameEx;
unsigned int FileNameExSize;
unsigned int Reserved[982];
};
struct RAROpenArchiveData
{
char *ArcName;
unsigned int OpenMode;
unsigned int OpenResult;
char *CmtBuf;
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
};
typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
#define ROADF_VOLUME 0x0001
#define ROADF_COMMENT 0x0002
#define ROADF_LOCK 0x0004
#define ROADF_SOLID 0x0008
#define ROADF_NEWNUMBERING 0x0010
#define ROADF_SIGNED 0x0020
#define ROADF_RECOVERY 0x0040
#define ROADF_ENCHEADERS 0x0080
#define ROADF_FIRSTVOLUME 0x0100
#define ROADOF_KEEPBROKEN 0x0001
struct RAROpenArchiveDataEx
{
char *ArcName;
wchar_t *ArcNameW;
unsigned int OpenMode;
unsigned int OpenResult;
char *CmtBuf;
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
unsigned int Flags;
UNRARCALLBACK Callback;
LPARAM UserData;
unsigned int OpFlags;
wchar_t *CmtBufW;
wchar_t *MarkOfTheWeb;
unsigned int Reserved[23];
};
enum UNRARCALLBACK_MESSAGES {
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
UCM_NEEDPASSWORDW,UCM_LARGEDICT
};
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
#ifdef __cplusplus
extern "C" {
#endif
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData);
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData);
int PASCAL RARCloseArchive(HANDLE hArcData);
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData);
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName);
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData);
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc);
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc);
void PASCAL RARSetPassword(HANDLE hArcData,char *Password);
int PASCAL RARGetDllVersion();
#ifdef __cplusplus
}
#endif
#pragma pack(pop)
#endif
+28
View File
@@ -0,0 +1,28 @@
#include <windows.h>
#include <commctrl.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 7, 20, 1, 1764
PRODUCTVERSION 7, 20, 1, 1764
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
{
BLOCK "040904E4"
{
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
VALUE "FileVersion", "7.20.1\0"
VALUE "ProductVersion", "7.20.1\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409, 0x04E4
}
}
+13
View File
@@ -0,0 +1,13 @@
EXPORTS
RAROpenArchive
RAROpenArchiveEx
RARCloseArchive
RARReadHeader
RARReadHeaderEx
RARProcessFile
RARProcessFileW
RARSetCallback
RARSetChangeVolProc
RARSetProcessDataProc
; RARSetPassword
RARGetDllVersion
+77
View File
@@ -0,0 +1,77 @@
#include "rar.hpp"
EncodeFileName::EncodeFileName()
{
Flags=0;
FlagBits=0;
FlagsPos=0;
DestSize=0;
}
void EncodeFileName::Decode(const char *Name,size_t NameSize,
const byte *EncName,size_t EncSize,
std::wstring &NameW)
{
size_t EncPos=0,DecPos=0;
byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
while (EncPos<EncSize)
{
if (FlagBits==0)
{
Flags=EncName[EncPos++];
FlagBits=8;
}
switch(Flags>>6)
{
case 0:
if (EncPos>=EncSize)
break;
// We need DecPos also for ASCII "Name", so resize() instead of push_back().
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos++];
break;
case 1:
if (EncPos>=EncSize)
break;
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
break;
case 2:
if (EncPos+1>=EncSize)
break;
NameW.resize(DecPos+1);
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
EncPos+=2;
break;
case 3:
{
if (EncPos>=EncSize)
break;
int Length=EncName[EncPos++];
if ((Length & 0x80)!=0)
{
if (EncPos>=EncSize)
break;
byte Correction=EncName[EncPos++];
for (Length=(Length&0x7f)+2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
}
}
else
for (Length+=2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
NameW[DecPos]=Name[DecPos];
}
}
break;
}
Flags<<=2;
FlagBits-=2;
}
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef _RAR_ENCNAME_
#define _RAR_ENCNAME_
class EncodeFileName
{
private:
void AddFlags(byte Value,std::vector<byte> &EncName);
byte Flags;
uint FlagBits;
size_t FlagsPos;
size_t DestSize;
public:
EncodeFileName();
void Encode(const std::string &Name,const std::wstring &NameW,std::vector<byte> &EncName);
void Decode(const char *Name,size_t NameSize,const byte *EncName,size_t EncSize,std::wstring &NameW);
};
#endif
+433
View File
@@ -0,0 +1,433 @@
#include "rar.hpp"
ErrorHandler::ErrorHandler()
{
Clean();
}
void ErrorHandler::Clean()
{
ExitCode=RARX_SUCCESS;
ErrCount=0;
EnableBreak=true;
Silent=false;
UserBreak=false;
MainExit=false;
DisableShutdown=false;
ReadErrIgnoreAll=false;
}
void ErrorHandler::MemoryError()
{
MemoryErrorMsg();
Exit(RARX_MEMORY);
}
void ErrorHandler::OpenError(const std::wstring &FileName)
{
#ifndef SILENT
OpenErrorMsg(FileName);
Exit(RARX_OPEN);
#endif
}
void ErrorHandler::CloseError(const std::wstring &FileName)
{
if (!UserBreak)
{
uiMsg(UIERROR_FILECLOSE,FileName);
SysErrMsg();
}
// We must not call Exit and throw an exception here, because this function
// is called from File object destructor and can be invoked when stack
// unwinding while handling another exception. Throwing a new exception
// when stack unwinding is prohibited and terminates a program.
// If necessary, we can check std::uncaught_exception() before throw.
SetErrorCode(RARX_FATAL);
}
void ErrorHandler::ReadError(const std::wstring &FileName)
{
#ifndef SILENT
ReadErrorMsg(FileName);
#endif
#if !defined(SILENT) || defined(RARDLL)
Exit(RARX_READ);
#endif
}
void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit)
{
SetErrorCode(RARX_READ);
#if !defined(SILENT) && !defined(SFX_MODULE)
if (!Silent)
{
uiMsg(UIERROR_FILEREAD,L"",FileName);
SysErrMsg();
if (ReadErrIgnoreAll)
Ignore=true;
else
{
bool All=false;
uiAskRepeatRead(FileName,Ignore,All,Retry,Quit);
if (All)
ReadErrIgnoreAll=Ignore=true;
if (Quit) // Disable shutdown if user select Quit in read error prompt.
DisableShutdown=true;
}
return;
}
#endif
Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice.
}
void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &FileName)
{
#ifndef SILENT
WriteErrorMsg(ArcName,FileName);
#endif
#if !defined(SILENT) || defined(RARDLL)
Exit(RARX_WRITE);
#endif
}
#ifdef _WIN_ALL
void ErrorHandler::WriteErrorFAT(const std::wstring &FileName)
{
SysErrMsg();
uiMsg(UIERROR_NTFSREQUIRED,FileName);
#if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL)
Exit(RARX_WRITE);
#endif
}
#endif
bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull)
{
#ifndef SILENT
if (!Silent)
{
// We do not display "repeat write" prompt in Android, so we do not
// need the matching system error message.
SysErrMsg();
bool Repeat=uiAskRepeatWrite(FileName,DiskFull);
if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
DisableShutdown=true;
return Repeat;
}
#endif
return false;
}
void ErrorHandler::SeekError(const std::wstring &FileName)
{
if (!UserBreak)
{
uiMsg(UIERROR_FILESEEK,FileName);
SysErrMsg();
}
#if !defined(SILENT) || defined(RARDLL)
Exit(RARX_FATAL);
#endif
}
void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
{
#ifndef RARDLL
va_list arglist;
va_start(arglist,fmt);
std::wstring Msg=vwstrprintf(fmt,arglist);
uiMsg(UIERROR_GENERALERRMSG,Msg);
SysErrMsg();
va_end(arglist);
#endif
}
void ErrorHandler::MemoryErrorMsg()
{
uiMsg(UIERROR_MEMORY);
SetErrorCode(RARX_MEMORY);
}
void ErrorHandler::OpenErrorMsg(const std::wstring &FileName)
{
OpenErrorMsg(L"",FileName);
}
void ErrorHandler::OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_OPEN);
// Keep GUI responsive if many files cannot be opened when archiving.
// Call after SysErrMsg to avoid modifying the error code and SysErrMsg text.
Wait();
}
void ErrorHandler::CreateErrorMsg(const std::wstring &FileName)
{
CreateErrorMsg(L"",FileName);
}
void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILECREATE,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_CREATE);
}
void ErrorHandler::ReadErrorMsg(const std::wstring &FileName)
{
ReadErrorMsg(L"",FileName);
}
void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILEREAD,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_READ);
}
void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
SysErrMsg();
SetErrorCode(RARX_WRITE);
}
void ErrorHandler::ArcBrokenMsg(const std::wstring &ArcName)
{
uiMsg(UIERROR_ARCBROKEN,ArcName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName)
{
uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
ErrHandler.SetErrorCode(RARX_FATAL);
}
void ErrorHandler::Exit(RAR_EXIT ExitCode)
{
uiAlarm(UIALARM_ERROR);
Throw(ExitCode);
}
void ErrorHandler::SetErrorCode(RAR_EXIT Code)
{
switch(Code)
{
case RARX_WARNING:
case RARX_USERBREAK:
if (ExitCode==RARX_SUCCESS)
ExitCode=Code;
break;
case RARX_CRC:
// 2025.10.25: RARX_OPEN is set if next volume is missing.
if (ExitCode!=RARX_BADPWD && ExitCode!=RARX_OPEN)
ExitCode=Code;
break;
case RARX_FATAL:
if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING)
ExitCode=RARX_FATAL;
break;
default:
ExitCode=Code;
break;
}
ErrCount++;
}
#ifdef _WIN_ALL
BOOL __stdcall ProcessSignal(DWORD SigType)
#else
#if defined(__sun)
extern "C"
#endif
void _stdfunction ProcessSignal(int SigType)
#endif
{
#ifdef _WIN_ALL
// When a console application is run as a service, this allows the service
// to continue running after the user logs off.
if (SigType==CTRL_LOGOFF_EVENT)
return TRUE;
#endif
ErrHandler.UserBreak=true;
ErrHandler.SetDisableShutdown();
mprintf(St(MBreak));
#ifdef _WIN_ALL
// Let the main thread to handle 'throw' and destroy file objects.
for (uint I=0;!ErrHandler.MainExit && I<50;I++)
Sleep(100);
#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL)
ExtRes.UnloadDLL();
#endif
exit(RARX_USERBREAK);
#endif
#ifdef _UNIX
static uint BreakCount=0;
// User continues to press Ctrl+C, exit immediately without cleanup.
if (++BreakCount>1)
exit(RARX_USERBREAK);
// Otherwise return from signal handler and let Wait() function to close
// files and quit. We cannot use the same approach as in Windows,
// because Unix signal handler can block execution of our main code.
#endif
#if defined(_WIN_ALL) && !defined(_MSC_VER)
// Never reached, just to avoid a compiler warning
return TRUE;
#endif
}
void ErrorHandler::SetSignalHandlers(bool Enable)
{
EnableBreak=Enable;
#ifdef _WIN_ALL
SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
#else
signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
#endif
}
void ErrorHandler::Throw(RAR_EXIT Code)
{
if (Code==RARX_USERBREAK && !EnableBreak)
return;
#if !defined(SILENT)
if (Code!=RARX_SUCCESS)
if (Code==RARX_USERERROR) // Do not write "aborted" when just displaying the online help.
mprintf(L"\n"); // For consistency with other errors, which print the final "\n".
else
mprintf(L"\n%s\n",St(MProgAborted));
#endif
SetErrorCode(Code);
throw Code;
}
bool ErrorHandler::GetSysErrMsg(std::wstring &Msg)
{
#ifndef SILENT
#ifdef _WIN_ALL
int ErrType=GetLastError();
if (ErrType!=0)
{
wchar *Buf=nullptr;
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|
FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)&Buf,0,NULL)!=0)
{
Msg=Buf;
LocalFree(Buf);
return true;
}
}
#endif
#ifdef _UNIX
if (errno!=0)
{
char *err=strerror(errno);
if (err!=NULL)
{
CharToWide(err,Msg);
return true;
}
}
#endif
#endif
return false;
}
void ErrorHandler::SysErrMsg()
{
#ifndef SILENT
std::wstring Msg;
if (!GetSysErrMsg(Msg))
return;
#ifdef _WIN_ALL
// Print string with \r\n as several strings to multiple lines.
size_t Pos=0;
while (Pos!=std::wstring::npos)
{
while (Msg[Pos]=='\r' || Msg[Pos]=='\n')
Pos++;
if (Pos==Msg.size())
break;
size_t EndPos=Msg.find_first_of(L"\r\n",Pos);
std::wstring CurMsg=Msg.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos);
uiMsg(UIERROR_SYSERRMSG,CurMsg);
Pos=EndPos;
}
#endif
#ifdef _UNIX
uiMsg(UIERROR_SYSERRMSG,Msg);
#endif
#endif
}
int ErrorHandler::GetSystemErrorCode()
{
#ifdef _WIN_ALL
return GetLastError();
#else
return errno;
#endif
}
void ErrorHandler::SetSystemErrorCode(int Code)
{
#ifdef _WIN_ALL
SetLastError(Code);
#else
errno=Code;
#endif
}
+76
View File
@@ -0,0 +1,76 @@
#ifndef _RAR_ERRHANDLER_
#define _RAR_ERRHANDLER_
enum RAR_EXIT // RAR exit code.
{
RARX_SUCCESS = 0,
RARX_WARNING = 1,
RARX_FATAL = 2,
RARX_CRC = 3,
RARX_LOCK = 4,
RARX_WRITE = 5,
RARX_OPEN = 6,
RARX_USERERROR = 7,
RARX_MEMORY = 8,
RARX_CREATE = 9,
RARX_NOFILES = 10,
RARX_BADPWD = 11,
RARX_READ = 12,
RARX_BADARC = 13,
RARX_USERBREAK = 255
};
class ErrorHandler
{
private:
RAR_EXIT ExitCode;
uint ErrCount;
bool EnableBreak;
bool Silent;
bool DisableShutdown; // Shutdown is not suitable after last error.
bool ReadErrIgnoreAll;
public:
ErrorHandler();
void Clean();
void MemoryError();
void OpenError(const std::wstring &FileName);
void CloseError(const std::wstring &FileName);
void ReadError(const std::wstring &FileName);
void AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit);
void WriteError(const std::wstring &ArcName,const std::wstring &FileName);
void WriteErrorFAT(const std::wstring &FileName);
bool AskRepeatWrite(const std::wstring &FileName,bool DiskFull);
void SeekError(const std::wstring &FileName);
void GeneralErrMsg(const wchar *fmt,...);
void MemoryErrorMsg();
void OpenErrorMsg(const std::wstring &FileName);
void OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void CreateErrorMsg(const std::wstring &FileName);
void CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void ReadErrorMsg(const std::wstring &FileName);
void ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
void ArcBrokenMsg(const std::wstring &ArcName);
void ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName);
void UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName);
void Exit(RAR_EXIT ExitCode);
void SetErrorCode(RAR_EXIT Code);
RAR_EXIT GetErrorCode() {return ExitCode;}
uint GetErrorCount() {return ErrCount;}
void SetSignalHandlers(bool Enable);
void Throw(RAR_EXIT Code);
void SetSilent(bool Mode) {Silent=Mode;}
bool GetSysErrMsg(std::wstring &Msg);
void SysErrMsg();
int GetSystemErrorCode();
void SetSystemErrorCode(int Code);
void SetDisableShutdown() {DisableShutdown=true;}
bool IsShutdownEnabled() {return !DisableShutdown;}
bool UserBreak; // Ctrl+Break is pressed.
bool MainExit; // main() is completed.
};
#endif
+184
View File
@@ -0,0 +1,184 @@
#include "rar.hpp"
#include "hardlinks.cpp"
#include "win32stm.cpp"
#ifdef _WIN_ALL
#include "win32acl.cpp"
#include "win32lnk.cpp"
#endif
#ifdef _UNIX
#include "uowners.cpp"
#ifdef SAVE_LINKS
#include "ulinks.cpp"
#endif
#endif
// RAR2 service header extra records.
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
break;
case STREAM_HEAD:
ExtractStreams20(Arc,Name);
break;
}
#endif
}
#endif
// RAR3 and RAR5 service header extra records.
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
ExtractUnixOwner30(Arc,Name.c_str());
#endif
#ifdef _WIN_ALL
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
ExtractACL(Arc,Name);
if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
ExtractStreams(Arc,Name,Cmd->Test);
#endif
}
// Extra data stored directly in file header.
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
SetUnixOwner(Arc,Name);
#endif
}
// Calculate the number of path components except \. and \..
static int CalcAllowedDepth(const std::wstring &Name)
{
int AllowedDepth=0;
for (size_t I=0;I<Name.size();I++)
if (IsPathDiv(Name[I]))
{
bool Dot=Name[I+1]=='.' && (IsPathDiv(Name[I+2]) || Name[I+2]==0);
bool Dot2=Name[I+1]=='.' && Name[I+2]=='.' && (IsPathDiv(Name[I+3]) || Name[I+3]==0);
if (!Dot && !Dot2)
AllowedDepth++;
else
if (Dot2)
AllowedDepth--;
}
return AllowedDepth < 0 ? 0 : AllowedDepth;
}
// Check if all existing path components are directories and not links.
static bool LinkInPath(std::wstring Path)
{
if (Path.empty()) // So we can safely use Path.size()-1 below.
return false;
for (size_t I=Path.size()-1;I>0;I--)
if (IsPathDiv(Path[I]))
{
Path.erase(I);
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
return true;
}
return false;
}
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
// Do not check PrepSrcName here, it can be root based if destination path
// is a root based.
if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
return false;
// Number of ".." in link target.
int UpLevels=0;
for (uint Pos=0;Pos<TargetName.size();Pos++)
{
bool Dot2=TargetName[Pos]=='.' && TargetName[Pos+1]=='.' &&
(IsPathDiv(TargetName[Pos+2]) || TargetName[Pos+2]==0) &&
(Pos==0 || IsPathDiv(TargetName[Pos-1]));
if (Dot2)
UpLevels++;
}
// If link target includes "..", it must not have another links in its
// source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
// or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
// file "dir/lnk1/lnk2/poc.txt" last.
// Do not confuse with link chains in target, this is in link source path.
// It is important for Windows too, though this check can be omitted
// if LinksToDirs is invoked in Windows as well.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;
// We could check just prepared src name, but for extra safety
// we check both original (as from archive header) and prepared
// (after applying the destination path and -ep switches) names.
int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
// Remove the destination path from prepared name if any. We should not
// count the destination path depth, because the link target must point
// inside of this path, not outside of it.
size_t ExtrPathLength=Cmd->ExtrPath.size();
if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0)
{
while (IsPathDiv(PrepSrcName[ExtrPathLength]))
ExtrPathLength++;
PrepSrcName.erase(0,ExtrPathLength);
}
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
}
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink)
{
// Returning true in Uplink indicates that link target might include ".."
// and enables additional checks. It is ok to falsely return true here,
// as it implies only the minor performance penalty. But we shall always
// return true for links with ".." in target for security reason.
UpLink=true; // Assume the target might include potentially unsafe "..".
#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
UpLink=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos;
#endif
#if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15)
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName.c_str(),UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead);
#elif defined(_WIN_ALL)
// RAR 5.0 archives store link information in file header, so there is
// no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50)
return CreateReparsePoint(Cmd,LinkName.c_str(),&Arc.FileHead);
#endif
return false;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const std::wstring &FileName);
#endif
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting);
std::wstring GetStreamNameNTFS(Archive &Arc);
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
#endif
+1768
View File
File diff suppressed because it is too large. Load diff
+96
View File
@@ -0,0 +1,96 @@
#ifndef _RAR_EXTRACT_
#define _RAR_EXTRACT_
enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
struct ExtractRef
{
std::wstring RefName;
std::wstring TmpName;
uint64 RefCount;
};
std::vector<ExtractRef> RefList;
struct AnalyzeData
{
std::wstring StartName;
uint64 StartPos;
std::wstring EndName;
uint64 EndPos;
} Analyze;
bool ArcAnalyzed;
void FreeAnalyzeData();
EXTRACT_ARC_CODE ExtractArchive();
bool ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize);
void ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName);
#ifdef RARDLL
bool ExtrDllGetPassword();
#else
bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
#endif
void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName);
bool ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly);
bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName);
#ifndef SFX_MODULE
void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering);
void GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName);
#endif
bool CheckWinLimit(Archive &Arc,std::wstring &ArcFileName);
RarTime StartTime; // Time when extraction started.
CommandData *Cmd;
ComprDataIO DataIO;
Unpack *Unp;
unsigned long TotalFileCount;
unsigned long FileCount;
unsigned long MatchedArgs;
bool FirstFile;
bool AllMatchesExact;
bool ReconstructDone;
bool UseExactVolName;
// If any non-zero solid file was successfully unpacked before current.
// If true and if current encrypted file is broken, obviously
// the password is correct and we can report broken CRC without
// any wrong password hints.
bool AnySolidDataUnpackedWell;
std::wstring ArcName;
bool GlobalPassword;
bool PrevProcessed; // If previous file was successfully extracted or tested.
std::wstring DestFileName;
bool SuppressNoFilesMessage;
// In Windows it is set to true if at least one symlink with ".."
// in target was extracted.
bool ConvertSymlinkPaths;
// Last path checked for symlinks. We use it to improve the performance,
// so we do not check recently checked folders again.
std::wstring LastCheckedSymlink;
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif
public:
CmdExtract(CommandData *Cmd);
~CmdExtract();
void DoExtract();
void ExtractArchiveInit(Archive &Arc);
bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
};
#endif
+150
View File
@@ -0,0 +1,150 @@
#include "rar.hpp"
// If NewFile==NULL, we delete created file after user confirmation.
// It is useful if we need to overwrite an existing folder or file,
// but need user confirmation for that.
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
*UserReject=false;
#ifdef _WIN_ALL
bool ShortNameChanged=false;
#endif
while (FileExist(Name))
{
#if defined(_WIN_ALL)
if (!ShortNameChanged)
{
// Avoid the infinite loop if UpdateExistingShortName returns
// the same name.
ShortNameChanged=true;
// Maybe our long name matches the short name of existing file.
// Let's check if we can change the short name.
if (UpdateExistingShortName(Name))
continue;
}
// Allow short name check again. It is necessary, because rename and
// autorename below can change the name, so we need to check it again.
ShortNameChanged=false;
#endif
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
if (Choice==UIASKREP_R_REPLACE)
break;
if (Choice==UIASKREP_R_SKIP)
{
if (UserReject!=NULL)
*UserReject=true;
return false;
}
if (Choice==UIASKREP_R_CANCEL)
ErrHandler.Exit(RARX_USERBREAK);
}
// Try to truncate the existing file first instead of delete,
// so we preserve existing file permissions, such as NTFS permissions,
// also as "Compressed" attribute and hard links. In GUI version we avoid
// deleting an existing file for non-.rar archive formats as well.
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
if (NewFile!=NULL && NewFile->Create(Name,FileMode))
return true;
CreatePath(Name,true,Cmd->DisableNames);
return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
}
#if defined(_WIN_ALL)
// If we find a file, which short name is equal to 'Name', we try to change
// its short name, while preserving the long name. It helps when unpacking
// an archived file, which long name is equal to short name of already
// existing file. Otherwise we would overwrite the already existing file,
// even though its long name does not match the name of unpacking file.
bool UpdateExistingShortName(const std::wstring &Name)
{
DWORD Res=GetLongPathName(Name.c_str(),NULL,0);
if (Res==0)
return false;
std::vector<wchar> LongPathBuf(Res);
Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size());
if (Res==0 || Res>=LongPathBuf.size())
return false;
Res=GetShortPathName(Name.c_str(),NULL,0);
if (Res==0)
return false;
std::vector<wchar> ShortPathBuf(Res);
Res=GetShortPathName(Name.c_str(),ShortPathBuf.data(),(DWORD)ShortPathBuf.size());
if (Res==0 || Res>=ShortPathBuf.size())
return false;
std::wstring LongPathName=LongPathBuf.data();
std::wstring ShortPathName=ShortPathBuf.data();
std::wstring LongName=PointToName(LongPathName);
std::wstring ShortName=PointToName(ShortPathName);
// We continue only if file has a short name, which does not match its
// long name, and this short name is equal to name of file which we need
// to create.
if (ShortName.empty() || wcsicomp(LongName,ShortName)==0 ||
wcsicomp(PointToName(Name),ShortName)!=0)
return false;
// Generate the temporary new name for existing file.
std::wstring NewName;
for (uint I=0;I<10000 && NewName.empty();I+=123)
{
// Here we copy the path part of file to create. We'll make the temporary
// file in the same folder.
NewName=Name;
// Here we set the random name part.
SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I));
// If such file is already exist, try next random name.
if (FileExist(NewName))
NewName.clear();
}
// If we could not generate the name not used by any other file, we return.
if (NewName.empty())
return false;
// FastFind returns the name without path, but we need the fully qualified
// name for renaming, so we use the path from file to create and long name
// from existing file.
std::wstring FullName=Name;
SetName(FullName,LongName);
// Rename the existing file to randomly generated name. Normally it changes
// the short name too.
if (!MoveFile(FullName.c_str(),NewName.c_str()))
return false;
// Now we need to create the temporary empty file with same name as
// short name of our already existing file. We do it to occupy its previous
// short name and not allow to use it again when renaming the file back to
// its original long name.
File KeepShortFile;
bool Created=false;
if (!FileExist(Name))
Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
// Now we rename the existing file from temporary name to original long name.
// Since its previous short name is occupied by another file, it should
// get another short name.
MoveFile(NewName.c_str(),FullName.c_str());
if (Created)
{
// Delete the temporary zero length file occupying the short name,
KeepShortFile.Close();
KeepShortFile.Delete();
}
// We successfully changed the short name. We do not use the simpler
// SetFileShortName Windows API call, because it requires SE_RESTORE_NAME
// privilege.
return true;
}
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
bool *UserReject,int64 FileSize=INT64NDF,
RarTime *FileTime=NULL,bool WriteOnly=false);
#if defined(_WIN_ALL)
bool UpdateExistingShortName(const std::wstring &Name);
#endif
#endif
+855
View File
@@ -0,0 +1,855 @@
#include "rar.hpp"
File::File()
{
hFile=FILE_BAD_HANDLE;
NewFile=false;
LastWrite=false;
HandleType=FILE_HANDLENORMAL;
LineInput=false;
SkipClose=false;
ErrorType=FILE_SUCCESS;
OpenShared=false;
AllowDelete=true;
AllowExceptions=true;
PreserveAtime=false;
#ifdef _WIN_ALL
// CreateMode=FMF_UNDEFINED;
#endif
ReadErrorMode=FREM_ASK;
TruncatedAfterReadError=false;
CurFilePos=0;
}
File::~File()
{
if (hFile!=FILE_BAD_HANDLE && !SkipClose)
if (NewFile)
Delete();
else
Close();
}
void File::operator = (File &SrcFile)
{
hFile=SrcFile.hFile;
NewFile=SrcFile.NewFile;
LastWrite=SrcFile.LastWrite;
HandleType=SrcFile.HandleType;
TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
FileName=SrcFile.FileName;
SrcFile.SkipClose=true;
}
bool File::Open(const std::wstring &Name,uint Mode)
{
ErrorType=FILE_SUCCESS;
FileHandle hNewFile;
bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
bool UpdateMode=(Mode & FMF_UPDATE)!=0;
bool WriteMode=(Mode & FMF_WRITE)!=0;
#ifdef _WIN_ALL
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
if (UpdateMode)
Access|=GENERIC_WRITE;
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
if (OpenShared)
ShareMode|=FILE_SHARE_WRITE;
uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
FindData FD;
if (PreserveAtime)
Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
hNewFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
DWORD LastError;
if (hNewFile==FILE_BAD_HANDLE)
{
LastError=GetLastError();
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
{
hNewFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
// For archive names longer than 260 characters first CreateFile
// (without \\?\) fails and sets LastError to 3 (access denied).
// We need the correct "file not found" error code to decide
// if we create a new archive or quit with "cannot create" error.
// So we need to check the error code after \\?\ CreateFile again,
// otherwise we'll fail to create new archives with long names.
// But we cannot simply assign the new code to LastError,
// because it would break "..\arcname.rar" relative names processing.
// First CreateFile returns the correct "file not found" code for such
// names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
// dots as a directory name. So we check only for "file not found"
// error here and for other errors use the first CreateFile result.
if (GetLastError()==ERROR_FILE_NOT_FOUND)
LastError=ERROR_FILE_NOT_FOUND;
}
}
if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
ErrorType=FILE_NOTFOUND;
if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
{
FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
SetFileTime(hNewFile,NULL,&ft,NULL);
}
#else
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
// 2025.06.09: We can't just set O_DIRECT for Unix like we set
// FILE_FLAG_SEQUENTIAL_SCAN for Windows to minimize disk caching.
// O_DIRECT might impose alignment requirements for data size, data address
// and file offset. Also it might not be supported by some file systems
// and fail with an error.
#ifdef O_BINARY
flags|=O_BINARY;
#if defined(_AIX) && defined(_LARGE_FILE_API)
flags|=O_LARGEFILE;
#endif
#endif
// NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
#if defined(O_NOATIME)
if (PreserveAtime)
flags|=O_NOATIME;
#endif
std::string NameA;
WideToChar(Name,NameA);
int handle=open(NameA.c_str(),flags);
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
extern "C" int flock(int, int);
#endif
if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
{
close(handle);
return false;
}
#endif
if (handle==-1)
hNewFile=FILE_BAD_HANDLE;
else
{
#ifdef FILE_USE_OPEN
hNewFile=handle;
#else
hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
#endif
}
if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
ErrorType=FILE_NOTFOUND;
#endif
NewFile=false;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
bool Success=hNewFile!=FILE_BAD_HANDLE;
if (Success)
{
hFile=hNewFile;
FileName=Name;
TruncatedAfterReadError=false;
}
return Success;
}
#if !defined(SFX_MODULE)
void File::TOpen(const std::wstring &Name)
{
if (!WOpen(Name))
ErrHandler.Exit(RARX_OPEN);
}
#endif
bool File::WOpen(const std::wstring &Name)
{
if (Open(Name))
return true;
ErrHandler.OpenErrorMsg(Name);
return false;
}
bool File::Create(const std::wstring &Name,uint Mode)
{
// 2025.09.03: Likely outdated info, see https://www.illumos.org/issues/2000
// OpenIndiana based NAS and CIFS shares fail to set the file time if file
// was created in read+write mode and some data was written and not flushed
// before SetFileTime call. So we should use the write only mode if we plan
// SetFileTime call and do not need to read from file.
bool WriteMode=(Mode & FMF_WRITE)!=0;
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
#ifdef _WIN_ALL
// CreateMode=Mode;
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
// Windows automatically removes dots and spaces in the end of file name,
// So we detect such names and process them with \\?\ prefix.
wchar LastChar=GetLastChar(Name);
bool Special=LastChar=='.' || LastChar==' ';
if (Special && (Mode & FMF_STANDARDNAMES)==0)
hFile=FILE_BAD_HANDLE;
else
hFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
if (hFile==FILE_BAD_HANDLE)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
}
#else
#ifdef FILE_USE_OPEN
std::string NameA;
WideToChar(Name,NameA);
hFile=open(NameA.c_str(),(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
#else
hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY);
#endif
#endif
NewFile=true;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
FileName=Name;
return hFile!=FILE_BAD_HANDLE;
}
#if !defined(SFX_MODULE)
void File::TCreate(const std::wstring &Name,uint Mode)
{
if (!WCreate(Name,Mode))
ErrHandler.Exit(RARX_FATAL);
}
#endif
bool File::WCreate(const std::wstring &Name,uint Mode)
{
if (Create(Name,Mode))
return true;
ErrHandler.CreateErrorMsg(Name);
return false;
}
bool File::Close()
{
bool Success=true;
if (hFile!=FILE_BAD_HANDLE)
{
if (!SkipClose)
{
#ifdef _WIN_ALL
// We use the standard system handle for stdout in Windows
// and it must not be closed here.
if (HandleType==FILE_HANDLENORMAL)
Success=CloseHandle(hFile)!=FALSE;
#else
#ifdef FILE_USE_OPEN
Success=close(hFile)!=-1;
#else
Success=fclose(hFile)!=EOF;
#endif
#endif
}
hFile=FILE_BAD_HANDLE;
}
HandleType=FILE_HANDLENORMAL;
if (!Success && AllowExceptions)
ErrHandler.CloseError(FileName);
return Success;
}
bool File::Delete()
{
if (HandleType!=FILE_HANDLENORMAL)
return false;
if (hFile!=FILE_BAD_HANDLE)
Close();
if (!AllowDelete)
return false;
return DelFile(FileName);
}
bool File::Rename(const std::wstring &NewName)
{
// No need to rename if names are already same.
bool Success=(NewName==FileName);
if (!Success)
Success=RenameFile(FileName,NewName);
if (Success)
FileName=NewName;
return Success;
}
bool File::Write(const void *Data,size_t Size)
{
if (Size==0)
return true;
if (HandleType==FILE_HANDLESTD)
{
#ifdef _WIN_ALL
hFile=GetStdHandle(STD_OUTPUT_HANDLE);
#else
// Cannot use the standard stdout here, because it already has wide orientation.
if (hFile==FILE_BAD_HANDLE)
{
#ifdef FILE_USE_OPEN
hFile=dup(STDOUT_FILENO); // Open new stdout stream.
#else
hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
#endif
}
#endif
}
bool Success;
while (1)
{
Success=false;
#ifdef _WIN_ALL
DWORD Written=0;
if (HandleType!=FILE_HANDLENORMAL)
{
// writing to stdout can fail in old Windows if data block is too large
const size_t MaxSize=0x4000;
for (size_t I=0;I<Size;I+=MaxSize)
{
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)!=FALSE;
if (!Success)
break;
}
}
else
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)!=FALSE;
#else
#ifdef FILE_USE_OPEN
ssize_t Written=write(hFile,Data,Size);
Success=Written==Size;
#else
int Written=fwrite(Data,1,Size,hFile);
Success=Written==Size && !ferror(hFile);
#endif
#endif
if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
{
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
int ErrCode=GetLastError();
int64 FilePos=Tell();
uint64 FreeSize=GetFreeDisk(FileName);
SetLastError(ErrCode);
if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
ErrHandler.WriteErrorFAT(FileName);
#endif
if (ErrHandler.AskRepeatWrite(FileName,false))
{
#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
clearerr(hFile);
#endif
if (Written<Size && Written>0)
Seek(Tell()-Written,SEEK_SET);
continue;
}
ErrHandler.WriteError(L"",FileName);
}
break;
}
LastWrite=true;
return Success; // It can return false only if AllowExceptions is disabled.
}
int File::Read(void *Data,size_t Size)
{
if (TruncatedAfterReadError)
return 0;
int64 FilePos=0; // Initialized only to suppress some compilers warning.
if (ReadErrorMode==FREM_IGNORE)
FilePos=Tell();
int TotalRead=0;
while (true)
{
int ReadSize=DirectRead(Data,Size);
if (ReadSize==-1)
{
ErrorType=FILE_READERROR;
if (AllowExceptions)
if (ReadErrorMode==FREM_IGNORE)
{
ReadSize=0;
for (size_t I=0;I<Size;I+=512)
{
Seek(FilePos+I,SEEK_SET);
size_t SizeToRead=Min(Size-I,512);
int ReadCode=DirectRead(Data,SizeToRead);
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
if (ReadSize!=-1)
TotalRead+=ReadSize;
}
}
else
{
bool Ignore=false,Retry=false,Quit=false;
if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL && IsOpened())
{
ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
if (Retry)
continue;
}
if (Ignore || ReadErrorMode==FREM_TRUNCATE)
{
TruncatedAfterReadError=true;
return 0;
}
ErrHandler.ReadError(FileName);
}
}
TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here.
if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size)
{
// Unlike regular files, for pipe we can read only as much as was
// written at the other end of pipe. We had seen data coming in small
// ~80 byte chunks when piping from 'type arc.rar'. Extraction code
// would fail if we read an incomplete archive header from stdin.
// So here we ensure that requested size is completely read.
// But we return the available data immediately in "line input" mode,
// when processing user's input in console prompts. Otherwise apps
// piping user responses to multiple Ask() prompts can hang if no more
// data is available yet and pipe isn't closed.
Data=(byte*)Data+ReadSize;
Size-=ReadSize;
continue;
}
break;
}
if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled.
CurFilePos+=TotalRead;
return TotalRead; // It can return -1 only if AllowExceptions is disabled.
}
// Returns -1 in case of error.
int File::DirectRead(void *Data,size_t Size)
{
#ifdef _WIN_ALL
const size_t MaxDeviceRead=20000;
const size_t MaxLockedRead=32768;
#endif
if (HandleType==FILE_HANDLESTD)
{
#ifdef _WIN_ALL
// if (Size>MaxDeviceRead)
// Size=MaxDeviceRead;
hFile=GetStdHandle(STD_INPUT_HANDLE);
#else
#ifdef FILE_USE_OPEN
hFile=STDIN_FILENO;
#else
hFile=stdin;
#endif
#endif
}
#ifdef _WIN_ALL
// For pipes like 'type file.txt | rar -si arcname' ReadFile may return
// data in small ~4KB blocks. It may slightly reduce the compression ratio.
DWORD Read;
if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
{
if (IsDevice() && Size>MaxDeviceRead)
return DirectRead(Data,MaxDeviceRead);
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
return 0;
// We had a bug report about failure to archive 1C database lock file
// 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
// permanently locked. If our first read request uses too large buffer
// and if we are in -dh mode, so we were able to open the file,
// we'll fail with "Read error". So now we use try a smaller buffer size
// in case of lock error.
if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
GetLastError()==ERROR_LOCK_VIOLATION)
return DirectRead(Data,MaxLockedRead);
return -1;
}
return Read;
#else
#ifdef FILE_USE_OPEN
ssize_t ReadSize=read(hFile,Data,Size);
if (ReadSize==-1)
return -1;
return (int)ReadSize;
#else
if (LastWrite)
{
fflush(hFile);
LastWrite=false;
}
clearerr(hFile);
size_t ReadSize=fread(Data,1,Size,hFile);
if (ferror(hFile))
return -1;
return (int)ReadSize;
#endif
#endif
}
void File::Seek(int64 Offset,int Method)
{
if (!RawSeek(Offset,Method) && AllowExceptions)
ErrHandler.SeekError(FileName);
}
bool File::RawSeek(int64 Offset,int Method)
{
if (hFile==FILE_BAD_HANDLE)
return true;
if (!IsSeekable()) // To extract archives from stdin with -si.
{
// We tried to dynamically allocate 32 KB buffer here, but it improved
// speed in Windows 10 by mere ~1.5%.
byte Buf[4096];
if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
{
uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
while (SkipSize>0) // Reading to emulate seek forward.
{
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
if (ReadSize<=0)
return false;
SkipSize-=ReadSize;
CurFilePos+=ReadSize;
}
return true;
}
// May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking
// RAR 4.x archives without the end of archive block created with -en.
if (Method==SEEK_END)
{
int ReadSize;
while ((ReadSize=Read(Buf,ASIZE(Buf)))>0)
CurFilePos+=ReadSize;
return true;
}
return false; // Backward seek on unseekable file.
}
if (Offset<0 && Method!=SEEK_SET)
{
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
Method=SEEK_SET;
}
#ifdef _WIN_ALL
LONG HighDist=(LONG)(Offset>>32);
if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
GetLastError()!=NO_ERROR)
return false;
#else
LastWrite=false;
#ifdef FILE_USE_OPEN
if (lseek(hFile,(off_t)Offset,Method)==-1)
return false;
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
if (fseeko(hFile,Offset,Method)!=0)
return false;
#else
if (fseek(hFile,(long)Offset,Method)!=0)
return false;
#endif
#endif
return true;
}
int64 File::Tell()
{
if (hFile==FILE_BAD_HANDLE)
if (AllowExceptions)
ErrHandler.SeekError(FileName);
else
return -1;
if (!IsSeekable())
return CurFilePos;
#ifdef _WIN_ALL
LONG HighDist=0;
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
if (AllowExceptions)
ErrHandler.SeekError(FileName);
else
return -1;
return INT32TO64(HighDist,LowDist);
#else
#ifdef FILE_USE_OPEN
return lseek(hFile,0,SEEK_CUR);
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
return ftello(hFile);
#else
return ftell(hFile);
#endif
#endif
}
void File::Prealloc(int64 Size)
{
#ifdef _WIN_ALL
if (RawSeek(Size,SEEK_SET))
{
Truncate();
Seek(0,SEEK_SET);
}
#endif
#if defined(_UNIX) && defined(USE_FALLOCATE)
// fallocate is rather new call. Only latest kernels support it.
// So we are not using it by default yet.
int fd = GetFD();
if (fd >= 0)
fallocate(fd, 0, 0, Size);
#endif
}
byte File::GetByte()
{
byte Byte=0;
Read(&Byte,1);
return Byte;
}
void File::PutByte(byte Byte)
{
Write(&Byte,1);
}
bool File::Truncate()
{
#ifdef _WIN_ALL
return SetEndOfFile(hFile)!=FALSE;
#else
return ftruncate(GetFD(),(off_t)Tell())==0;
#endif
}
void File::Flush()
{
#ifdef _WIN_ALL
FlushFileBuffers(hFile);
#else
#ifndef FILE_USE_OPEN
fflush(hFile);
#endif
fsync(GetFD());
#endif
}
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#ifdef _WIN_ALL
// Workaround for OpenIndiana NAS time bug. If we cannot create a file
// in write only mode, we need to flush the write buffer before calling
// SetFileTime or file time will not be changed.
// 2025.09.03: Removed this code as likely redundant now,
// see https://www.illumos.org/issues/2000
// if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
// FlushFileBuffers(hFile);
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
FILETIME fm,fc,fa;
if (sm)
ftm->GetWinFT(&fm);
if (sc)
ftc->GetWinFT(&fc);
if (sa)
fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
#endif
}
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
{
// Android APP_PLATFORM := android-14 does not support futimens and futimes.
// Newer platforms support futimens, but fail on Android 4.2.
// We have to use utime for Android.
// Also we noticed futimens fail to set timestamps on NTFS partition
// mounted to virtual Linux x86 machine, but utimensat worked correctly.
// So we set timestamps for already closed files in Unix.
#ifdef _UNIX
SetCloseFileTimeByName(FileName,ftm,fta);
#endif
}
void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta)
{
#ifdef _UNIX
bool setm=ftm!=NULL && ftm->IsSet();
bool seta=fta!=NULL && fta->IsSet();
if (setm || seta)
{
std::string NameA;
WideToChar(Name,NameA);
#ifdef UNIX_TIME_NS
timespec times[2];
times[0].tv_sec=seta ? fta->GetUnix() : 0;
times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
times[1].tv_sec=setm ? ftm->GetUnix() : 0;
times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
utimensat(AT_FDCWD,NameA.c_str(),times,0);
#else
utimbuf ut;
if (setm)
ut.modtime=ftm->GetUnix();
else
ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
if (seta)
ut.actime=fta->GetUnix();
else
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
utime(NameA.c_str(),&ut);
#endif
}
#endif
}
#ifdef _UNIX
void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#ifdef UNIX_TIME_NS
#if defined(_APPLE)
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
#else
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
#endif
#else
if (ftm!=NULL) ftm->SetUnix(st.st_mtime);
if (ftc!=NULL) ftc->SetUnix(st.st_ctime);
if (fta!=NULL) fta->SetUnix(st.st_atime);
#endif
}
#endif
void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#ifdef _WIN_ALL
FILETIME ctime,atime,mtime;
GetFileTime(hFile,&ctime,&atime,&mtime);
if (ftm!=NULL) ftm->SetWinFT(&mtime);
if (ftc!=NULL) ftc->SetWinFT(&ctime);
if (fta!=NULL) fta->SetWinFT(&atime);
#elif defined(_UNIX)
struct stat st;
fstat(GetFD(),&st);
StatToRarTime(st,ftm,ftc,fta);
#endif
}
int64 File::FileLength()
{
int64 SavePos=Tell();
Seek(0,SEEK_END);
int64 Length=Tell();
Seek(SavePos,SEEK_SET);
return Length;
}
bool File::IsDevice()
{
if (hFile==FILE_BAD_HANDLE)
return false;
#ifdef _WIN_ALL
uint Type=GetFileType(hFile);
return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
#else
return isatty(GetFD());
#endif
}
#ifndef SFX_MODULE
int64 File::Copy(File &Dest,int64 Length)
{
bool CopyAll=(Length==INT64NDF);
// Adjust the buffer size to data size. So we do not waste too much time
// to vector initialization when copying many small data blocks like
// when updating an archive with many small files.
size_t BufSize=File::CopyBufferSize();
if (!CopyAll && Length<(int64)BufSize)
BufSize=(size_t)Length;
std::vector<byte> Buffer(BufSize);
int64 CopySize=0;
while (CopyAll || Length>0)
{
Wait();
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size();
byte *Buf=Buffer.data();
int ReadSize=Read(Buf,SizeToRead);
if (ReadSize==0)
break;
size_t WriteSize=ReadSize;
#ifdef _WIN_ALL
// For FAT32 USB flash drives in Windows if first write is 4 KB or more,
// write caching is disabled and "write through" is enabled, resulting
// in bad performance, especially for many small files. It happens when
// we create SFX archive on USB drive, because SFX module is written first.
// So we split the first write to small 1 KB followed by rest of data.
if (CopySize==0 && WriteSize>=4096)
{
const size_t FirstWrite=1024;
Dest.Write(Buf,FirstWrite);
Buf+=FirstWrite;
WriteSize-=FirstWrite;
}
#endif
Dest.Write(Buf,WriteSize);
CopySize+=ReadSize;
if (!CopyAll)
Length-=ReadSize;
}
return CopySize;
}
#endif
+156
View File
@@ -0,0 +1,156 @@
#ifndef _RAR_FILE_
#define _RAR_FILE_
#define FILE_USE_OPEN
#ifdef _WIN_ALL
typedef HANDLE FileHandle;
#define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
#elif defined(FILE_USE_OPEN)
typedef off_t FileHandle;
#define FILE_BAD_HANDLE -1
#else
typedef FILE* FileHandle;
#define FILE_BAD_HANDLE NULL
#endif
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
enum FILE_MODE_FLAGS {
// Request read only access to file. Default for Open.
FMF_READ=0,
// Request both read and write access to file. Default for Create.
FMF_UPDATE=1,
// Request write only access to file.
FMF_WRITE=2,
// Open files which are already opened for write by other programs.
FMF_OPENSHARED=4,
// Open files only if no other program is opened it even in shared mode.
FMF_OPENEXCLUSIVE=8,
// Provide read access to created file for other programs.
FMF_SHAREREAD=16,
// Use standard NTFS names without trailing dots and spaces.
FMF_STANDARDNAMES=32,
// Mode flags are not defined yet.
// FMF_UNDEFINED=256
};
enum FILE_READ_ERROR_MODE {
FREM_ASK, // Propose to use the already read part, retry or abort.
FREM_TRUNCATE, // Use the already read part without additional prompt.
FREM_IGNORE // Try to skip unreadable block and read further.
};
class File
{
private:
FileHandle hFile;
bool LastWrite;
FILE_HANDLETYPE HandleType;
// If we read the user input in console prompts from stdin, we shall
// process the available line immediately, not waiting for rest of data.
// Otherwise apps piping user responses to multiple Ask() prompts can
// hang if no more data is available yet and pipe isn't closed.
// If we read RAR archive or other file data from stdin, we shall collect
// the entire requested block as long as pipe isn't closed, so we get
// complete archive headers, not split between different reads.
bool LineInput;
bool SkipClose;
FILE_READ_ERROR_MODE ReadErrorMode;
bool NewFile;
bool AllowDelete;
bool AllowExceptions;
#ifdef _WIN_ALL
// uint CreateMode;
#endif
bool PreserveAtime;
bool TruncatedAfterReadError;
int64 CurFilePos; // Used for forward seeks in stdin files.
protected:
bool OpenShared; // Set by 'Archive' class.
public:
std::wstring FileName;
FILE_ERRORTYPE ErrorType;
public:
File();
virtual ~File();
void operator = (File &SrcFile);
// Several functions below are 'virtual', because they are redefined
// by Archive for QOpen and by MultiFile for split files in WinRAR.
virtual bool Open(const std::wstring &Name,uint Mode=FMF_READ);
void TOpen(const std::wstring &Name);
bool WOpen(const std::wstring &Name);
bool Create(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
void TCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
bool WCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
virtual bool Close(); // 'virtual' for MultiFile class.
bool Delete();
bool Rename(const std::wstring &NewName);
bool Write(const void *Data,size_t Size);
virtual int Read(void *Data,size_t Size);
int DirectRead(void *Data,size_t Size);
virtual void Seek(int64 Offset,int Method);
bool RawSeek(int64 Offset,int Method);
virtual int64 Tell();
void Prealloc(int64 Size);
byte GetByte();
void PutByte(byte Byte);
bool Truncate();
void Flush();
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
static void SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta);
#ifdef _UNIX
static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta);
#endif
void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
virtual int64 FileLength(); // 'virtual' for MultiFile class.
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
void SetLineInputMode(bool Mode) {LineInput=Mode;}
FILE_HANDLETYPE GetHandleType() {return HandleType;}
bool IsSeekable() {return HandleType!=FILE_HANDLESTD;}
bool IsDevice();
static bool RemoveCreated();
FileHandle GetHandle() {return hFile;}
void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;}
int64 Copy(File &Dest,int64 Length=INT64NDF);
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;}
#ifdef _UNIX
int GetFD()
{
#ifdef FILE_USE_OPEN
return hFile;
#else
return fileno(hFile);
#endif
}
#endif
static size_t CopyBufferSize()
{
// Values in 0x100000 - 0x400000 range are ok, but multithreaded CRC32
// seems to benefit from 0x400000, especially on ARM CPUs.
return 0x400000;
}
};
#endif
+630
View File
@@ -0,0 +1,630 @@
#include "rar.hpp"
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
{
#ifdef _WIN_ALL
// Windows automatically removes dots and spaces in the end of directory
// name. So we detect such names and process them with \\?\ prefix.
wchar LastChar=GetLastChar(Name);
bool Special=LastChar=='.' || LastChar==' ';
BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL);
if (RetCode==0 && !FileExist(Name))
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
RetCode=CreateDirectory(LongName.c_str(),NULL);
}
if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
{
if (SetAttr)
SetFileAttr(Name,Attr);
return MKDIR_SUCCESS;
}
int ErrCode=GetLastError();
if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
return MKDIR_BADPATH;
return MKDIR_ERROR;
#elif defined(_UNIX)
std::string NameA;
WideToChar(Name,NameA);
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
int ErrCode=mkdir(NameA.c_str(),uattr);
if (ErrCode==-1)
return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
return MKDIR_SUCCESS;
#else
return MKDIR_ERROR;
#endif
}
// Simplified version of MakeDir().
bool CreateDir(const std::wstring &Name)
{
return MakeDir(Name,false,0)==MKDIR_SUCCESS;
}
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent)
{
if (Path.empty())
return false;
#ifdef _WIN_ALL
uint DirAttr=0;
#else
uint DirAttr=0777;
#endif
bool Success=true;
for (size_t I=0;I<Path.size();I++)
{
// Process all kinds of path separators, so user can enter Unix style
// path in Windows or Windows in Unix. I>0 check avoids attempting
// creating an empty directory for paths starting from path separator.
if (IsPathDiv(Path[I]) && I>0)
{
#ifdef _WIN_ALL
// We must not attempt to create "D:" directory, because first
// CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
// to create "D:" directory.
if (I==2 && Path[1]==':')
continue;
#endif
std::wstring DirName=Path.substr(0,I);
Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
if (Success && !Silent)
{
mprintf(St(MCreatDir),DirName.c_str());
mprintf(L" %s",St(MOk));
}
}
}
if (!SkipLastName && !IsPathDiv(GetLastChar(Path)))
Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
return Success;
}
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#if defined(_WIN_ALL)
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
uint DirAttr=GetFileAttr(Name);
bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
if (ResetAttr)
SetFileAttr(Name,0);
HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
}
if (hFile==INVALID_HANDLE_VALUE)
return;
FILETIME fm,fc,fa;
if (sm)
ftm->GetWinFT(&fm);
if (sc)
ftc->GetWinFT(&fc);
if (sa)
fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
CloseHandle(hFile);
if (ResetAttr)
SetFileAttr(Name,DirAttr);
#endif
#ifdef _UNIX
File::SetCloseFileTimeByName(Name,ftm,fta);
#endif
}
bool IsRemovable(const std::wstring &Name)
{
#if defined(_WIN_ALL)
std::wstring Root;
GetPathRoot(Name,Root);
int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str());
return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
#else
return false;
#endif
}
#ifndef SFX_MODULE
int64 GetFreeDisk(const std::wstring &Name)
{
#ifdef _WIN_ALL
std::wstring Root;
GetPathWithSep(Name,Root);
ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) &&
uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
return 0;
#elif defined(_UNIX)
std::wstring Root;
GetPathWithSep(Name,Root);
std::string RootA;
WideToChar(Root,RootA);
struct statvfs sfs;
if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0)
return 0;
int64 FreeSize=sfs.f_bsize;
FreeSize=FreeSize*sfs.f_bavail;
return FreeSize;
#else
return 0;
#endif
}
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
// file size to 4 GB for these file systems.
bool IsFAT(const std::wstring &Name)
{
std::wstring Root;
GetPathRoot(Name,Root);
wchar FileSystem[MAX_PATH+1];
// Root can be empty, when we create volumes with -v in the current folder.
if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
return false;
}
#endif
bool FileExist(const std::wstring &Name)
{
#ifdef _WIN_ALL
return GetFileAttr(Name)!=0xffffffff;
#elif defined(ENABLE_ACCESS)
std::string NameA;
WideToChar(Name,NameA);
return access(NameA.c_str(),0)==0;
#else
FindData FD;
return FindFile::FastFind(Name,&FD);
#endif
}
bool WildFileExist(const std::wstring &Name)
{
if (IsWildcard(Name))
{
FindFile Find;
Find.SetMask(Name);
FindData fd;
return Find.Next(&fd);
}
return FileExist(Name);
}
bool IsDir(uint Attr)
{
#ifdef _WIN_ALL
return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
#endif
#if defined(_UNIX)
return (Attr & 0xF000)==0x4000;
#endif
}
bool IsUnreadable(uint Attr)
{
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
#else
return false;
#endif
}
bool IsLink(uint Attr)
{
#ifdef _UNIX
return (Attr & 0xF000)==0xA000;
#elif defined(_WIN_ALL)
return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
#else
return false;
#endif
}
bool IsDeleteAllowed(uint FileAttr)
{
#ifdef _WIN_ALL
return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
#else
return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
#endif
}
void PrepareToDelete(const std::wstring &Name)
{
#ifdef _WIN_ALL
SetFileAttr(Name,0);
#endif
#ifdef _UNIX
std::string NameA;
WideToChar(Name,NameA);
chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR);
#endif
}
uint GetFileAttr(const std::wstring &Name)
{
#ifdef _WIN_ALL
DWORD Attr=GetFileAttributes(Name.c_str());
if (Attr==0xffffffff)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Attr=GetFileAttributes(LongName.c_str());
}
return Attr;
#else
std::string NameA;
WideToChar(Name,NameA);
struct stat st;
if (stat(NameA.c_str(),&st)!=0)
return 0;
return st.st_mode;
#endif
}
bool SetFileAttr(const std::wstring &Name,uint Attr)
{
#ifdef _WIN_ALL
bool Success=SetFileAttributes(Name.c_str(),Attr)!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=SetFileAttributes(LongName.c_str(),Attr)!=0;
}
return Success;
#elif defined(_UNIX)
std::string NameA;
WideToChar(Name,NameA);
return chmod(NameA.c_str(),(mode_t)Attr)==0;
#else
return false;
#endif
}
// Ext is the extension with the leading dot, like L".bat", or nullptr to use
// the default extension.
bool MkTemp(std::wstring &Name,const wchar *Ext)
{
RarTime CurTime;
CurTime.SetCurrentTime();
// We cannot use CurTime.GetWin() as is, because its lowest bits can
// have low informational value, like being a zero or few fixed numbers.
uint Random=(uint)(CurTime.GetWin()/100000);
// Using PID we guarantee that different RAR copies use different temp names
// even if started in exactly the same time.
uint PID=0;
#ifdef _WIN_ALL
PID=(uint)GetCurrentProcessId();
#elif defined(_UNIX)
PID=(uint)getpid();
#endif
for (uint Attempt=0;;Attempt++)
{
uint RandomExt=Random%50000+Attempt;
if (Attempt==1000)
return false;
// User asked to specify the single extension for all temporary files,
// so it can be added to server ransomware protection exceptions.
// He wrote, this protection blocks temporary files when adding
// a file to RAR archive with drag and drop. So unless a calling code
// requires a specific extension, like .bat file when uninstalling,
// we set the uniform extension here.
if (Ext==nullptr)
Ext=L".rartemp";
std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext;
if (!FileExist(NewName))
{
Name=NewName;
break;
}
}
return true;
}
#if !defined(SFX_MODULE)
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
{
int64 SavePos=SrcFile->Tell();
#ifndef SILENT
int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
#endif
if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
uiMsg(UIEVENT_FILESUMSTART);
if ((Flags & CALCFSUM_CURPOS)==0)
SrcFile->Seek(0,SEEK_SET);
const size_t BufSize=0x100000;
std::vector<byte> Data(BufSize);
DataHash HashCRC,HashBlake2;
HashCRC.Init(HASH_CRC32,Threads);
HashBlake2.Init(HASH_BLAKE2,Threads);
int64 BlockCount=0;
int64 TotalRead=0;
while (true)
{
size_t SizeToRead;
if (Size==INT64NDF) // If we process the entire file.
SizeToRead=BufSize; // Then always attempt to read the entire buffer.
else
SizeToRead=(size_t)Min((int64)BufSize,Size);
int ReadSize=SrcFile->Read(Data.data(),SizeToRead);
if (ReadSize==0)
break;
TotalRead+=ReadSize;
if ((++BlockCount & 0xf)==0)
{
#ifndef SILENT
if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
{
// Update only the current file progress in WinRAR, set the total to 0
// to keep it as is. It looks better for WinRAR.
uiExtractProgress(TotalRead,FileLength,0,0);
}
else
{
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
}
#endif
Wait();
}
if (CRC32!=NULL)
HashCRC.Update(Data.data(),ReadSize);
if (Blake2!=NULL)
HashBlake2.Update(Data.data(),ReadSize);
if (Size!=INT64NDF)
Size-=ReadSize;
}
SrcFile->Seek(SavePos,SEEK_SET);
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
uiMsg(UIEVENT_FILESUMEND);
if (CRC32!=NULL)
*CRC32=HashCRC.GetCRC32();
if (Blake2!=NULL)
{
HashValue Result;
HashBlake2.Result(&Result);
memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
}
}
#endif
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName)
{
#ifdef _WIN_ALL
bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0;
if (!Success)
{
std::wstring LongName1,LongName2;
if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2))
Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0;
}
return Success;
#else
std::string SrcNameA,DestNameA;
WideToChar(SrcName,SrcNameA);
WideToChar(DestName,DestNameA);
bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0;
return Success;
#endif
}
bool DelFile(const std::wstring &Name)
{
#ifdef _WIN_ALL
bool Success=DeleteFile(Name.c_str())!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=DeleteFile(LongName.c_str())!=0;
}
return Success;
#else
std::string NameA;
WideToChar(Name,NameA);
bool Success=remove(NameA.c_str())==0;
return Success;
#endif
}
bool DelDir(const std::wstring &Name)
{
#ifdef _WIN_ALL
bool Success=RemoveDirectory(Name.c_str())!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=RemoveDirectory(LongName.c_str())!=0;
}
return Success;
#else
std::string NameA;
WideToChar(Name,NameA);
bool Success=rmdir(NameA.c_str())==0;
return Success;
#endif
}
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool SetFileCompression(const std::wstring &Name,bool State)
{
HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
return false;
}
bool Success=SetFileCompression(hFile,State);
CloseHandle(hFile);
return Success;
}
bool SetFileCompression(HANDLE hFile,bool State)
{
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
DWORD Result;
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
sizeof(NewState),NULL,0,&Result,NULL);
return RetCode!=0;
}
void ResetFileCache(const std::wstring &Name)
{
// To reset file cache in Windows it is enough to open it with
// FILE_FLAG_NO_BUFFERING and then close it.
HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL);
if (hSrc!=INVALID_HANDLE_VALUE)
CloseHandle(hSrc);
}
#endif
// Delete symbolic links in file path, if any, and replace them by directories.
// Prevents extracting files outside of destination folder with symlink chains.
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked)
{
// Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
// "lnk1/../dir", but converts the path to "dir". In Unix we need to call
// this function to prevent placing unpacked files outside of destination
// folder if previously we unpacked "dir/lnk1" -> "..",
// "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
// We may still need this function to prevent abusing symlink chains
// in link source path if we remove detection of such chains
// in IsRelativeSymlinkSafe. This function seems to make other symlink
// related safety checks redundant, but for now we prefer to keep them too.
//
// 2022.12.01: the performance impact is minimized after adding the check
// against the previous path and enabling this verification only after
// extracting a symlink with ".." in target. So we enabled it for Windows
// as well for extra safety.
//#ifdef _UNIX
std::wstring Path=SrcName;
size_t SkipLength=SkipPart.size();
if (SkipLength>0 && !starts_with(Path,SkipPart))
SkipLength=0; // Parameter validation, not really needed now.
// Do not check parts already checked in previous path to improve performance.
for (size_t I=0;I<Path.size() && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
if (IsPathDiv(Path[I]) && I>SkipLength)
SkipLength=I;
// Avoid converting symlinks in destination path part specified by user.
while (SkipLength<Path.size() && IsPathDiv(Path[SkipLength]))
SkipLength++;
if (Path.size()>0)
for (size_t I=Path.size()-1;I>SkipLength;I--)
if (IsPathDiv(Path[I]))
{
Path.erase(I);
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
{
#ifdef _WIN_ALL
// Normally Windows symlinks to directory look like a directory
// and are deleted with DelDir(). It is possible to create
// a file-like symlink pointing at directory, which can be deleted
// only with && DelFile, but such symlink isn't really functional.
// Here we prefer to fail deleting such symlink and skip extracting
// a file.
if (!DelDir(Path))
#else
if (!DelFile(Path))
#endif
{
ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory.
return false; // Couldn't delete the symlink to replace it with directory.
}
}
}
LastChecked=SrcName;
//#endif
return true;
}
+57
View File
@@ -0,0 +1,57 @@
#ifndef _RAR_FILEFN_
#define _RAR_FILEFN_
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr);
bool CreateDir(const std::wstring &Name);
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent);
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
bool IsRemovable(const std::wstring &Name);
#ifndef SFX_MODULE
int64 GetFreeDisk(const std::wstring &Name);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool IsFAT(const std::wstring &Root);
#endif
bool FileExist(const std::wstring &Name);
bool WildFileExist(const std::wstring &Name);
bool IsDir(uint Attr);
bool IsUnreadable(uint Attr);
bool IsLink(uint Attr);
void SetSFXMode(const std::wstring &FileName);
void EraseDiskContents(const std::wstring &FileName);
bool IsDeleteAllowed(uint FileAttr);
void PrepareToDelete(const std::wstring &Name);
uint GetFileAttr(const std::wstring &Name);
bool SetFileAttr(const std::wstring &Name,uint Attr);
bool MkTemp(std::wstring &Name,const wchar *Ext);
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName);
bool DelFile(const std::wstring &Name);
bool DelDir(const std::wstring &Name);
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool SetFileCompression(const std::wstring &Name,bool State);
bool SetFileCompression(HANDLE hFile,bool State);
void ResetFileCache(const std::wstring &Name);
#endif
// Keep it here and not in extinfo.cpp, because it is invoked from Zip.SFX too.
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked);
#endif
+163
View File
@@ -0,0 +1,163 @@
#include "rar.hpp"
bool ReadTextFile(
const std::wstring &Name,
StringList *List,
bool Config,
bool AbortOnError,
RAR_CHARSET SrcCharset,
bool Unquote,
bool SkipComments,
bool ExpandEnvStr)
{
std::wstring FileName;
if (Config)
GetConfigName(Name,FileName,true,false);
else
FileName=Name;
File SrcFile;
if (!FileName.empty())
{
bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
if (!OpenCode)
{
if (AbortOnError)
ErrHandler.Exit(RARX_OPEN);
return false;
}
}
else
SrcFile.SetHandleType(FILE_HANDLESTD);
size_t DataSize=0,ReadSize;
const int ReadBlock=4096;
std::vector<byte> Data(ReadBlock);
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
{
DataSize+=ReadSize;
Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data.
}
// Set to really read size, so we can zero terminate it correctly.
Data.resize(DataSize);
int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
if (SrcCharset==RCH_DEFAULT)
SrcCharset=DetectTextEncoding(Data.data(),DataSize);
std::vector<wchar> DataW(ReadBlock);
if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
{
Data.push_back(0); // Zero terminate.
#if defined(_WIN_ALL)
if (SrcCharset==RCH_OEM)
OemToCharA((char *)Data.data(),(char *)Data.data());
#endif
DataW.resize(Data.size());
CharToWide((char *)Data.data(),DataW.data(),DataW.size());
}
if (SrcCharset==RCH_UNICODE)
{
size_t Start=2; // Skip byte order mark.
if (!LittleEndian && !BigEndian) // No byte order mask.
{
Start=0;
LittleEndian=1;
}
DataW.resize(Data.size()/2+1);
size_t End=Data.size() & ~1; // We need even bytes number for UTF-16.
for (size_t I=Start;I<End;I+=2)
DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256;
DataW[(End-Start)/2]=0;
}
if (SrcCharset==RCH_UTF8)
{
Data.push_back(0); // Zero terminate data.
DataW.resize(Data.size());
UtfToWide((const char *)(Data.data()+(Utf8 ? 3:0)),DataW.data(),DataW.size());
}
wchar *CurStr=DataW.data();
while (*CurStr!=0)
{
wchar *NextStr=CurStr,*CmtPtr=NULL;
while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
{
if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
{
*NextStr=0;
CmtPtr=NextStr;
}
NextStr++;
}
bool Done=*NextStr==0;
*NextStr=0;
for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
{
if (*SpacePtr!=' ' && *SpacePtr!='\t')
break;
*SpacePtr=0;
}
if (Unquote && *CurStr=='\"')
{
size_t Length=wcslen(CurStr);
if (CurStr[Length-1]=='\"')
{
CurStr[Length-1]=0;
CurStr++;
}
}
bool Expanded=false;
#if defined(_WIN_ALL)
if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
{
std::wstring ExpName=CurStr;
ExpandEnvironmentStr(ExpName);
if (!ExpName.empty())
List->AddString(ExpName);
Expanded=true;
}
#endif
if (!Expanded && *CurStr!=0)
List->AddString(CurStr);
if (Done)
break;
CurStr=NextStr+1;
while (*CurStr=='\r' || *CurStr=='\n')
CurStr++;
}
return true;
}
RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize)
{
if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf &&
IsTextUtf8(Data+3,DataSize-3))
return RCH_UTF8;
bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254;
bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255;
if (LittleEndian || BigEndian)
for (size_t I=LittleEndian ? 3 : 2;I<DataSize;I+=2)
if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n')
return RCH_UNICODE; // High byte in UTF-16 char is found.
return RCH_DEFAULT;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef _RAR_FILESTR_
#define _RAR_FILESTR_
bool ReadTextFile(
const std::wstring &Name,
StringList *List,
bool Config,
bool AbortOnError=false,
RAR_CHARSET SrcCharset=RCH_DEFAULT,
bool Unquote=false,
bool SkipComments=false,
bool ExpandEnvStr=false
);
RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize);
#endif
+208
View File
@@ -0,0 +1,208 @@
#include "rar.hpp"
FindFile::FindFile()
{
FirstCall=true;
#ifdef _WIN_ALL
hFind=INVALID_HANDLE_VALUE;
#else
dirp=NULL;
#endif
}
FindFile::~FindFile()
{
#ifdef _WIN_ALL
if (hFind!=INVALID_HANDLE_VALUE)
FindClose(hFind);
#else
if (dirp!=NULL)
closedir(dirp);
#endif
}
void FindFile::SetMask(const std::wstring &Mask)
{
FindMask=Mask;
FirstCall=true;
}
bool FindFile::Next(FindData *fd,bool GetSymLink)
{
fd->Error=false;
if (FindMask.empty())
return false;
#ifdef _WIN_ALL
if (FirstCall)
{
if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
return false;
}
else
if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
return false;
#else
if (FirstCall)
{
std::wstring DirName;
DirName=FindMask;
RemoveNameFromPath(DirName);
if (DirName.empty())
DirName=L".";
std::string DirNameA;
WideToChar(DirName,DirNameA);
if ((dirp=opendir(DirNameA.c_str()))==NULL)
{
fd->Error=(errno!=ENOENT);
return false;
}
}
while (1)
{
std::wstring Name;
struct dirent *ent=readdir(dirp);
if (ent==NULL)
return false;
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
if (!CharToWide(std::string(ent->d_name),Name))
uiMsg(UIERROR_INVALIDNAME,L"",Name);
if (CmpName(FindMask,Name,MATCH_NAMES))
{
std::wstring FullName=FindMask;
FullName.erase(GetNamePos(FullName));
if (FullName.size()+Name.size()>=MAXPATHSIZE)
{
uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
return false;
}
FullName+=Name;
if (!FastFind(FullName,fd,GetSymLink))
{
ErrHandler.OpenErrorMsg(FullName);
continue;
}
fd->Name=FullName;
break;
}
}
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
fd->IsLink=IsLink(fd->FileAttr);
FirstCall=false;
std::wstring NameOnly=PointToName(fd->Name);
if (NameOnly==L"." || NameOnly==L"..")
return Next(fd);
return true;
}
bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink)
{
fd->Error=false;
#ifndef _UNIX
if (IsWildcard(FindMask))
return false;
#endif
#ifdef _WIN_ALL
HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
if (hFind==INVALID_HANDLE_VALUE)
return false;
FindClose(hFind);
#elif defined(_UNIX)
std::string FindMaskA;
WideToChar(FindMask,FindMaskA);
struct stat st;
if (GetSymLink)
{
#ifdef SAVE_LINKS
if (lstat(FindMaskA.c_str(),&st)!=0)
#else
if (stat(FindMaskA.c_str(),&st)!=0)
#endif
{
fd->Error=(errno!=ENOENT);
return false;
}
}
else
if (stat(FindMaskA.c_str(),&st)!=0)
{
fd->Error=(errno!=ENOENT);
return false;
}
fd->FileAttr=st.st_mode;
fd->Size=st.st_size;
File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
fd->Name=FindMask;
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
fd->IsLink=IsLink(fd->FileAttr);
return true;
}
#ifdef _WIN_ALL
HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd)
{
WIN32_FIND_DATA FindData;
if (hFind==INVALID_HANDLE_VALUE)
{
hFind=FindFirstFile(Mask.c_str(),&FindData);
if (hFind==INVALID_HANDLE_VALUE)
{
std::wstring LongMask;
if (GetWinLongPath(Mask,LongMask))
hFind=FindFirstFile(LongMask.c_str(),&FindData);
}
if (hFind==INVALID_HANDLE_VALUE)
{
int SysErr=GetLastError();
// We must not issue an error for "file not found" and "path not found",
// because it is normal to not find anything for wildcard mask when
// archiving. Also searching for non-existent file is normal in some
// other modules, like WinRAR scanning for winrar_theme_description.txt
// to check if any themes are available.
fd->Error=SysErr!=ERROR_FILE_NOT_FOUND &&
SysErr!=ERROR_PATH_NOT_FOUND &&
SysErr!=ERROR_NO_MORE_FILES;
}
}
else
if (!FindNextFile(hFind,&FindData))
{
hFind=INVALID_HANDLE_VALUE;
fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
}
if (hFind!=INVALID_HANDLE_VALUE)
{
fd->Name=Mask;
SetName(fd->Name,FindData.cFileName);
fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
fd->FileAttr=FindData.dwFileAttributes;
fd->ftCreationTime=FindData.ftCreationTime;
fd->ftLastAccessTime=FindData.ftLastAccessTime;
fd->ftLastWriteTime=FindData.ftLastWriteTime;
fd->mtime.SetWinFT(&FindData.ftLastWriteTime);
fd->ctime.SetWinFT(&FindData.ftCreationTime);
fd->atime.SetWinFT(&FindData.ftLastAccessTime);
}
fd->Flags=0;
return hFind;
}
#endif
+49
View File
@@ -0,0 +1,49 @@
#ifndef _RAR_FINDDATA_
#define _RAR_FINDDATA_
enum FINDDATA_FLAGS {
FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
};
struct FindData
{
std::wstring Name;
uint64 Size;
uint FileAttr;
bool IsDir;
bool IsLink;
RarTime mtime;
RarTime ctime;
RarTime atime;
#ifdef _WIN_ALL
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
#endif
uint Flags;
bool Error;
};
class FindFile
{
private:
#ifdef _WIN_ALL
static HANDLE Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd);
#endif
std::wstring FindMask;
bool FirstCall;
#ifdef _WIN_ALL
HANDLE hFind;
#else
DIR *dirp;
#endif
public:
FindFile();
~FindFile();
void SetMask(const std::wstring &Mask);
bool Next(FindData *fd,bool GetSymLink=false);
static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false);
};
#endif
+52
View File
@@ -0,0 +1,52 @@
#include "rar.hpp"
BitInput::BitInput(bool AllocBuffer)
{
ExternalBuffer=false;
if (AllocBuffer)
{
// getbits*() attempt to read data from InAddr, ... InAddr+8 positions.
// So let's allocate 8 additional bytes for situation, when we need to
// read only 1 byte from the last position of buffer and avoid a crash
// from access to next 8 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+8;
InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area
// not filled with read data.
memset(InBuf,0,BufSize);
}
else
InBuf=nullptr;
}
BitInput::~BitInput()
{
if (!ExternalBuffer)
delete[] InBuf;
}
void BitInput::faddbits(uint Bits)
{
// Function wrapped version of inline addbits to reduce the code size.
addbits(Bits);
}
uint BitInput::fgetbits()
{
// Function wrapped version of inline getbits to reduce the code size.
return getbits();
}
void BitInput::SetExternalBuffer(byte *Buf)
{
if (InBuf!=nullptr && !ExternalBuffer)
delete[] InBuf;
InBuf=Buf;
ExternalBuffer=true;
}
+81
View File
@@ -0,0 +1,81 @@
#ifndef _RAR_GETBITS_
#define _RAR_GETBITS_
class BitInput
{
public:
enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
int InAddr; // Curent byte position in the buffer.
int InBit; // Current bit position in the current byte.
bool ExternalBuffer;
public:
BitInput(bool AllocBuffer);
~BitInput();
byte *InBuf; // Dynamically allocated input buffer.
void InitBitInput()
{
InAddr=InBit=0;
}
// Move forward by 'Bits' bits.
void addbits(uint Bits)
{
Bits+=InBit;
InAddr+=Bits>>3;
InBit=Bits&7;
}
// Return 16 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits()
{
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
uint32 BitField=RawGetBE4(InBuf+InAddr);
BitField >>= (16-InBit);
#else
uint BitField=(uint)InBuf[InAddr] << 16;
BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit);
#endif
return BitField & 0xffff;
}
// Return 32 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint getbits32()
{
uint BitField=RawGetBE4(InBuf+InAddr);
BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
return BitField & 0xffffffff;
}
// Return 64 bits from current position in the buffer.
// Bit at (InAddr,InBit) has the highest position in returning data.
uint64 getbits64()
{
uint64 BitField=RawGetBE8(InBuf+InAddr);
BitField <<= InBit;
BitField|=(uint)InBuf[InAddr+8] >> (8-InBit);
return BitField;
}
void faddbits(uint Bits);
uint fgetbits();
// Check if buffer has enough space for IncPtr bytes. Returns 'true'
// if buffer will be overflown.
bool Overflow(uint IncPtr)
{
return InAddr+IncPtr>=MAX_SIZE;
}
void SetExternalBuffer(byte *Buf);
};
#endif
+7
View File
@@ -0,0 +1,7 @@
#define INCLUDEGLOBAL
#ifdef _MSC_VER
#pragma hdrstop
#endif
#include "rar.hpp"
+14
View File
@@ -0,0 +1,14 @@
#ifndef _RAR_GLOBAL_
#define _RAR_GLOBAL_
#ifdef INCLUDEGLOBAL
#define EXTVAR
#else
#define EXTVAR extern
#endif
EXTVAR ErrorHandler ErrHandler;
#endif
+37
View File
@@ -0,0 +1,37 @@
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting)
{
if (!FileExist(NameExisting))
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
uiMsg(UIERROR_NOLINKTARGET);
ErrHandler.SetErrorCode(RARX_CREATE);
return false;
}
CreatePath(NameNew,true,Cmd->DisableNames);
#ifdef _WIN_ALL
bool Success=CreateHardLink(NameNew.c_str(),NameExisting.c_str(),NULL)!=0;
if (!Success)
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
ErrHandler.SysErrMsg();
ErrHandler.SetErrorCode(RARX_CREATE);
}
return Success;
#elif defined(_UNIX)
std::string NameExistingA,NameNewA;
WideToChar(NameExisting,NameExistingA);
WideToChar(NameNew,NameNewA);
bool Success=link(NameExistingA.c_str(),NameNewA.c_str())==0;
if (!Success)
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
ErrHandler.SysErrMsg();
ErrHandler.SetErrorCode(RARX_CREATE);
}
return Success;
#else
return false;
#endif
}
+283
View File
@@ -0,0 +1,283 @@
#include "rar.hpp"
void HashValue::Init(HASH_TYPE Type)
{
HashValue::Type=Type;
// Zero length data CRC32 is 0. It is important to set it when creating
// headers with no following data like directories or symlinks.
if (Type==HASH_RAR14 || Type==HASH_CRC32)
CRC32=0;
if (Type==HASH_BLAKE2)
{
// dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
// is BLAKE2sp hash of empty data. We init the structure to this value,
// so if we create a file or service header with no following data like
// "file copy" or "symlink", we set the checksum to proper value avoiding
// additional header type or size checks when extracting.
static byte EmptyHash[32]={
0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
};
memcpy(Digest,EmptyHash,sizeof(Digest));
}
}
bool HashValue::operator == (const HashValue &cmp) const
{
if (Type==HASH_NONE || cmp.Type==HASH_NONE)
return true;
if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
return CRC32==cmp.CRC32;
if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
return false;
}
DataHash::DataHash()
{
blake2ctx=NULL;
HashType=HASH_NONE;
#ifdef RAR_SMP
ThPool=NULL;
MaxThreads=0;
#endif
}
DataHash::~DataHash()
{
#ifdef RAR_SMP
delete ThPool;
#endif
cleandata(&CurCRC32, sizeof(CurCRC32));
if (blake2ctx!=NULL)
{
cleandata(blake2ctx, sizeof(blake2sp_state));
delete blake2ctx;
}
}
void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
{
if (blake2ctx==NULL)
blake2ctx=new blake2sp_state;
HashType=Type;
if (Type==HASH_RAR14)
CurCRC32=0;
if (Type==HASH_CRC32)
CurCRC32=0xffffffff; // Initial CRC32 value.
if (Type==HASH_BLAKE2)
blake2sp_init(blake2ctx);
#ifdef RAR_SMP
DataHash::MaxThreads=Min(MaxThreads,HASH_POOL_THREADS);
#endif
}
void DataHash::Update(const void *Data,size_t DataSize)
{
#ifndef SFX_MODULE
if (HashType==HASH_RAR14)
CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
#endif
if (HashType==HASH_CRC32)
{
#ifdef RAR_SMP
UpdateCRC32MT(Data,DataSize);
#else
CurCRC32=CRC32(CurCRC32,Data,DataSize);
#endif
}
if (HashType==HASH_BLAKE2)
{
#ifdef RAR_SMP
if (MaxThreads>1 && ThPool==nullptr)
ThPool=new ThreadPool(HASH_POOL_THREADS);
blake2ctx->ThPool=ThPool;
blake2ctx->MaxThreads=MaxThreads;
#endif
blake2sp_update( blake2ctx, (byte *)Data, DataSize);
}
}
#ifdef RAR_SMP
THREAD_PROC(BuildCRC32Thread)
{
DataHash::CRC32ThreadData *td=(DataHash::CRC32ThreadData *)Data;
// Use 0 initial value to simplify combining the result with existing CRC32.
// It doesn't affect the first initial 0xffffffff in the data beginning.
// If we used 0xffffffff here, we would need to shift 0xffffffff left to
// block width and XOR it with block CRC32 to reset its initial value to 0.
td->DataCRC=CRC32(0,td->Data,td->DataSize);
}
// CRC is linear and distributive over addition, so CRC(a+b)=CRC(a)+CRC(b).
// Since addition in finite field is XOR, we have CRC(a^b)=CRC(a)^CRC(b).
// So CRC(aaabbb) = CRC(aaa000) ^ CRC(000bbb) = CRC(aaa000) ^ CRC(bbb),
// because CRC ignores leading zeroes. Thus to split CRC calculations
// to "aaa" and "bbb" blocks and then to threads we need to be able to
// find CRC(aaa000) knowing "aaa" quickly. We use Galois finite field to
// calculate the power of 2 to get "1000" and multiply it by "aaa".
void DataHash::UpdateCRC32MT(const void *Data,size_t DataSize)
{
const size_t MinBlock=0x4000;
if (DataSize<2*MinBlock || MaxThreads<2)
{
CurCRC32=CRC32(CurCRC32,Data,DataSize);
return;
}
if (ThPool==nullptr)
ThPool=new ThreadPool(HASH_POOL_THREADS);
size_t Threads=MaxThreads;
size_t BlockSize=DataSize/Threads;
if (BlockSize<MinBlock)
{
BlockSize=MinBlock;
Threads=DataSize/BlockSize;
}
CRC32ThreadData td[MaxPoolThreads];
//#undef USE_THREADS
for (size_t I=0;I<Threads;I++)
{
td[I].Data=(byte*)Data+I*BlockSize;
td[I].DataSize=(I+1==Threads) ? DataSize-I*BlockSize : BlockSize;
#ifdef USE_THREADS
ThPool->AddTask(BuildCRC32Thread,(void*)&td[I]);
#else
BuildCRC32Thread((void*)&td[I]);
#endif
}
#ifdef USE_THREADS
ThPool->WaitDone();
#endif // USE_THREADS
uint StdShift=gfExpCRC(uint(8*td[0].DataSize));
for (size_t I=0;I<Threads;I++)
{
// Prepare the multiplier to shift CRC to proper position.
uint ShiftMult;
if (td[I].DataSize==td[0].DataSize)
ShiftMult=StdShift; // Reuse the shift value for typical block size.
else
ShiftMult=gfExpCRC(uint(8*td[I].DataSize)); // 2 power "shift bits".
// To combine the cumulative total and current block CRC32, we multiply
// the total data CRC32 to shift value to place it to proper position.
// Invoke BitReverse32(), because 0xEDB88320 is the reversed polynomial.
// Alternatively we could adjust the multiplication function for reversed
// polynomials, but it would make it less readable without real speed gain.
// If CRC32 threads used 0xffffffff initial value, we would need
// to XOR the total data CRC32 with 0xffffffff before multiplication,
// so 0xffffffff is also shifted left to current block width and replaces
// the initial 0xffffffff CRC32 value with 0 in the current block CRC32
// after XOR'ing it with total data CRC32. Since now CRC32 threads use 0
// initial value, this is not necessary.
CurCRC32=BitReverse32(gfMulCRC(BitReverse32(CurCRC32), ShiftMult));
// Combine the total data and current block CRC32.
CurCRC32^=td[I].DataCRC;
}
}
#endif
uint DataHash::BitReverse32(uint N)
{
uint Reversed=0;
for (uint I=0;I<32;I++,N>>=1)
Reversed|=(N & 1)<<(31-I);
return Reversed;
}
// Galois field multiplication modulo POLY.
uint DataHash::gfMulCRC(uint A, uint B)
{
// For reversed 0xEDB88320 polynomial we bit reverse CRC32 before passing
// to this function, so we must use the normal polynomial here.
// We set the highest polynomial bit 33 for proper multiplication
// in case uint is larger than 32-bit.
const uint POLY=uint(0x104c11db7);
uint R = 0 ; // Multiplication result.
while (A != 0 && B != 0) // If any of multipliers becomes 0, quit early.
{
// For non-zero lowest B bit, add A to result.
R ^= (B & 1)!=0 ? A : 0;
// Make A twice larger before the next iteration.
// Subtract POLY to keep it modulo POLY if high bit is set.
A = (A << 1) ^ ((A & 0x80000000)!=0 ? POLY : 0);
B >>= 1; // Move next B bit to lowest position.
}
return R;
}
// Calculate 2 power N with square-and-multiply algorithm.
uint DataHash::gfExpCRC(uint N)
{
uint S = 2; // Starts from base value and contains the current square.
uint R = 1; // Exponentiation result.
while (N > 1)
{
if ((N & 1)!=0) // If N is odd.
R = gfMulCRC(R, S);
S = gfMulCRC(S, S); // Next square.
N >>= 1;
}
// We could change the loop condition to N > 0 and return R at expense
// of one additional gfMulCRC(S, S).
return gfMulCRC(R, S);
}
void DataHash::Result(HashValue *Result)
{
Result->Type=HashType;
if (HashType==HASH_RAR14)
Result->CRC32=CurCRC32;
if (HashType==HASH_CRC32)
Result->CRC32=CurCRC32^0xffffffff;
if (HashType==HASH_BLAKE2)
{
// Preserve the original context, so we can continue hashing if necessary.
blake2sp_state res=*blake2ctx;
blake2sp_final(&res,Result->Digest);
}
}
uint DataHash::GetCRC32()
{
return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
}
bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
{
HashValue Final;
Result(&Final);
#ifndef RAR_NOCRYPT
if (Key!=nullptr)
ConvertHashToMAC(&Final,Key);
#endif
return Final==*CmpValue;
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef _RAR_DATAHASH_
#define _RAR_DATAHASH_
enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
struct HashValue
{
void Init(HASH_TYPE Type);
// Use the const member, so types on both sides of "==" match.
// Otherwise clang -std=c++20 issues "ambiguity is between a regular call
// to this operator and a call with the argument order reversed" warning.
bool operator == (const HashValue &cmp) const;
// Not actually used now. Const member for same reason as operator == above.
// Can be removed after switching to C++20, which automatically provides "!="
// if operator == is defined.
bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
HASH_TYPE Type;
union
{
uint CRC32;
byte Digest[SHA256_DIGEST_SIZE];
};
};
#ifdef RAR_SMP
class ThreadPool;
class DataHash;
#endif
class DataHash
{
public:
struct CRC32ThreadData
{
void *Data;
size_t DataSize;
uint DataCRC;
};
private:
void UpdateCRC32MT(const void *Data,size_t DataSize);
uint BitReverse32(uint N);
uint gfMulCRC(uint A, uint B);
uint gfExpCRC(uint N);
// Speed gain seems to vanish above 8 CRC32 threads.
static const uint CRC32_POOL_THREADS=8;
// Thread pool must allow at least BLAKE2_THREADS_NUMBER threads.
static const uint HASH_POOL_THREADS=Max(BLAKE2_THREADS_NUMBER,CRC32_POOL_THREADS);
HASH_TYPE HashType;
uint CurCRC32;
blake2sp_state *blake2ctx;
#ifdef RAR_SMP
ThreadPool *ThPool;
uint MaxThreads;
#endif
public:
DataHash();
~DataHash();
void Init(HASH_TYPE Type,uint MaxThreads);
void Update(const void *Data,size_t DataSize);
void Result(HashValue *Result);
uint GetCRC32();
bool Cmp(HashValue *CmpValue,byte *Key);
HASH_TYPE Type() {return HashType;}
};
#endif
+55
View File
@@ -0,0 +1,55 @@
#include "rar.hpp"
void FileHeader::Reset(size_t SubDataSize)
{
SubData.resize(SubDataSize);
BaseBlock::Reset();
FileHash.Init(HASH_NONE);
mtime.Reset();
atime.Reset();
ctime.Reset();
SplitBefore=false;
SplitAfter=false;
UnknownUnpSize=0;
SubFlags=0; // Important for RAR 3.0 subhead.
CryptMethod=CRYPT_NONE;
Encrypted=false;
SaltSet=false;
UsePswCheck=false;
UseHashKey=false;
Lg2Count=0;
Solid=false;
Dir=false;
WinSize=0;
Inherited=false;
SubBlock=false;
CommentInHeader=false;
Version=false;
LargeFile=false;
RedirType=FSREDIR_NONE;
DirTarget=false;
UnixOwnerSet=false;
}
/*
FileHeader& FileHeader::operator = (FileHeader &hd)
{
SubData.Reset();
memcpy(this,&hd,sizeof(*this));
SubData.CleanData();
SubData=hd.SubData;
return *this;
}
*/
void MainHeader::Reset()
{
*this={};
}
+357
View File
@@ -0,0 +1,357 @@
#ifndef _RAR_HEADERS_
#define _RAR_HEADERS_
#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header.
#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header.
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size.
#define SIZEOF_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26
#define SIZEOF_STREAMHEAD 26
#define VER_PACK 29U
#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive.
#define VER_PACK7 70U // It is stored as 1, but we subtract 70 when saving an archive.
#define VER_UNPACK 29U
#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive.
#define VER_UNPACK7 70U // It is stored as 1, but we add 50 when reading an archive.
#define VER_UNKNOWN 9999U // Just some large value.
#define MHD_VOLUME 0x0001U
// Old style main archive comment embed into main archive header. Must not
// be used in new archives anymore.
#define MHD_COMMENT 0x0002U
#define MHD_LOCK 0x0004U
#define MHD_SOLID 0x0008U
#define MHD_PACK_COMMENT 0x0010U
#define MHD_NEWNUMBERING 0x0010U
#define MHD_AV 0x0020U
#define MHD_PROTECT 0x0040U
#define MHD_PASSWORD 0x0080U
#define MHD_FIRSTVOLUME 0x0100U
#define LHD_SPLIT_BEFORE 0x0001U
#define LHD_SPLIT_AFTER 0x0002U
#define LHD_PASSWORD 0x0004U
// Old style file comment embed into file header. Must not be used
// in new archives anymore.
#define LHD_COMMENT 0x0008U
// For non-file subheaders it denotes 'subblock having a parent file' flag.
#define LHD_SOLID 0x0010U
#define LHD_WINDOWMASK 0x00e0U
#define LHD_WINDOW64 0x0000U
#define LHD_WINDOW128 0x0020U
#define LHD_WINDOW256 0x0040U
#define LHD_WINDOW512 0x0060U
#define LHD_WINDOW1024 0x0080U
#define LHD_WINDOW2048 0x00a0U
#define LHD_WINDOW4096 0x00c0U
#define LHD_DIRECTORY 0x00e0U
#define LHD_LARGE 0x0100U
#define LHD_UNICODE 0x0200U
#define LHD_SALT 0x0400U
#define LHD_VERSION 0x0800U
#define LHD_EXTTIME 0x1000U
#define SKIP_IF_UNKNOWN 0x4000U
#define LONG_BLOCK 0x8000U
#define EARC_NEXT_VOLUME 0x0001U // Not last volume.
#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record.
#define EARC_VOLNUMBER 0x0008U // Store a number of current volume.
enum HEADER_TYPE {
// RAR 5.0 header types.
HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,
// RAR 1.5 - 4.x header types.
HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
};
// RAR 2.9 and earlier service haeders, mostly outdated and not supported.
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
// Internal implementation, depends on archive format version.
enum HOST_SYSTEM {
// RAR 5.0 host OS
HOST5_WINDOWS=0,HOST5_UNIX=1,
// RAR 3.0 host OS.
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
HOST_BEOS=5,HOST_MAX
};
// Unified archive format independent implementation.
enum HOST_SYSTEM_TYPE {
HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
};
// We also use these values in extra field, so do not modify them.
enum FILE_SYSTEM_REDIRECT {
FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
FSREDIR_HARDLINK, FSREDIR_FILECOPY
};
#define SUBHEAD_TYPE_CMT L"CMT"
#define SUBHEAD_TYPE_QOPEN L"QO"
#define SUBHEAD_TYPE_ACL L"ACL"
#define SUBHEAD_TYPE_STREAM L"STM"
#define SUBHEAD_TYPE_UOWNER L"UOW"
#define SUBHEAD_TYPE_AV L"AV"
#define SUBHEAD_TYPE_RR L"RR"
#define SUBHEAD_TYPE_OS2EA L"EA2"
/* new file inherits a subblock when updating a host file */
#define SUBHEAD_FLAGS_INHERITED 0x80000000
#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
struct MarkHeader
{
byte Mark[8];
// Following fields are virtual and not present in real blocks.
uint HeadSize;
};
struct BaseBlock
{
uint HeadCRC; // 'ushort' for RAR 1.5.
HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
uint Flags; // 'ushort' for RAR 1.5.
uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.
bool SkipIfUnknown;
void Reset()
{
SkipIfUnknown=false;
}
// We use it to assign this block data to inherited blocks.
// Such function seems to be cleaner than '(BaseBlock&)' cast or adding
// 'using BaseBlock::operator=;' to every inherited header.
void SetBaseBlock(BaseBlock &Src)
{
*this=Src;
}
};
struct BlockHeader:BaseBlock
{
uint DataSize;
};
struct MainHeader:BaseBlock
{
ushort HighPosAV;
uint PosAV;
bool CommentInHeader;
bool PackComment; // For RAR 1.4 archive format only.
bool Locator;
uint64 QOpenOffset; // Offset of quick list record.
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
uint64 RROffset; // Offset of recovery record.
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
std::wstring OrigName; // Original archive name.
RarTime OrigTime; // Original archive time.
void Reset();
};
struct FileHeader:BlockHeader
{
byte HostOS;
uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
byte Method;
union {
uint FileAttr;
uint SubFlags;
};
std::wstring FileName;
std::vector<byte> SubData;
RarTime mtime;
RarTime ctime;
RarTime atime;
int64 PackSize;
int64 UnpSize;
int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.
HashValue FileHash;
uint FileFlags;
bool SplitBefore;
bool SplitAfter;
bool UnknownUnpSize;
bool Encrypted;
CRYPT_METHOD CryptMethod;
bool SaltSet;
byte Salt[SIZE_SALT50];
byte InitV[SIZE_INITV];
bool UsePswCheck;
byte PswCheck[SIZE_PSWCHECK];
// Use HMAC calculated from HashKey and checksum instead of plain checksum.
bool UseHashKey;
// Key to convert checksum to HMAC. Derived from password with PBKDF2
// using additional iterations.
byte HashKey[SHA256_DIGEST_SIZE];
uint Lg2Count; // Log2 of PBKDF2 repetition count.
bool Solid;
bool Dir;
bool CommentInHeader; // RAR 2.0 file comment.
bool Version; // name.ext;ver file name containing the version number.
uint64 WinSize;
bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
// 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
bool LargeFile;
// 'true' for HEAD_SERVICE block, which is a child of preceding file block.
// RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
bool SubBlock;
HOST_SYSTEM_TYPE HSType;
FILE_SYSTEM_REDIRECT RedirType;
std::wstring RedirName;
bool DirTarget;
bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
char UnixOwnerName[256],UnixGroupName[256];
#ifdef _UNIX
uid_t UnixOwnerID;
gid_t UnixGroupID;
#else // Need these Unix fields in Windows too for 'list' command.
uint UnixOwnerID;
uint UnixGroupID;
#endif
void Reset(size_t SubDataSize=0);
bool CmpName(const wchar *Name)
{
return FileName==Name;
}
// FileHeader& operator = (FileHeader &hd);
};
struct EndArcHeader:BaseBlock
{
// Optional CRC32 of entire archive up to start of EndArcHeader block.
// Present in RAR 4.x archives if EARC_DATACRC flag is set.
uint ArcDataCRC;
uint VolNumber; // Optional number of current volume.
// 7 additional zero bytes can be stored here if EARC_REVSPACE is set.
bool NextVolume; // Not last volume.
bool DataCRC;
bool RevSpace;
bool StoreVolNumber;
void Reset()
{
BaseBlock::Reset();
NextVolume=false;
DataCRC=false;
RevSpace=false;
StoreVolNumber=false;
}
};
struct CryptHeader:BaseBlock
{
bool UsePswCheck;
uint Lg2Count; // Log2 of PBKDF2 repetition count.
byte Salt[SIZE_SALT50];
byte PswCheck[SIZE_PSWCHECK];
};
// SubBlockHeader and its successors were used in RAR 2.x format.
// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
struct SubBlockHeader:BlockHeader
{
ushort SubType;
byte Level;
};
struct CommentHeader:BaseBlock
{
ushort UnpSize;
byte UnpVer;
byte Method;
ushort CommCRC;
};
struct ProtectHeader:BlockHeader
{
byte Version;
ushort RecSectors;
uint TotalBlocks;
byte Mark[8];
};
struct EAHeader:SubBlockHeader
{
uint UnpSize;
byte UnpVer;
byte Method;
uint EACRC;
};
struct StreamHeader:SubBlockHeader
{
uint UnpSize;
byte UnpVer;
byte Method;
uint StreamCRC;
ushort StreamNameSize;
std::string StreamName;
};
#endif
+114
View File
@@ -0,0 +1,114 @@
#ifndef _RAR_HEADERS5_
#define _RAR_HEADERS5_
#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
// RAR 5.0 block flags common for all blocks.
// Additional extra area is present in the end of block header.
#define HFL_EXTRA 0x0001
// Additional data area is present in the end of block header.
#define HFL_DATA 0x0002
// Unknown blocks with this flag must be skipped when updating an archive.
#define HFL_SKIPIFUNKNOWN 0x0004
// Data area of this block is continuing from previous volume.
#define HFL_SPLITBEFORE 0x0008
// Data area of this block is continuing in next volume.
#define HFL_SPLITAFTER 0x0010
// Block depends on preceding file block.
#define HFL_CHILD 0x0020
// Preserve a child block if host is modified.
#define HFL_INHERITED 0x0040
// RAR 5.0 main archive header specific flags.
#define MHFL_VOLUME 0x0001 // Volume.
#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
#define MHFL_SOLID 0x0004 // Solid archive.
#define MHFL_PROTECT 0x0008 // Recovery record is present.
#define MHFL_LOCK 0x0010 // Locked archive.
// RAR 5.0 file header specific flags.
#define FHFL_DIRECTORY 0x0001 // Directory.
#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
#define FHFL_CRC32 0x0004 // CRC32 field is present.
#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
// RAR 5.0 end of archive header specific flags.
#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
// RAR 5.0 archive encryption header specific flags.
#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
// RAR 5.0 file compression flags.
#define FCI_ALGO_BIT0 0x00000001 // Version of compression algorithm.
#define FCI_ALGO_BIT1 0x00000002 // 0 .. 63.
#define FCI_ALGO_BIT2 0x00000004
#define FCI_ALGO_BIT3 0x00000008
#define FCI_ALGO_BIT4 0x00000010
#define FCI_ALGO_BIT5 0x00000020
#define FCI_SOLID 0x00000040 // Solid flag.
#define FCI_METHOD_BIT0 0x00000080 // Compression method.
#define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used).
#define FCI_METHOD_BIT2 0x00000200
#define FCI_DICT_BIT0 0x00000400 // Dictionary size.
#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 1 TB.
#define FCI_DICT_BIT2 0x00001000
#define FCI_DICT_BIT3 0x00002000
#define FCI_DICT_BIT4 0x00004000
#define FCI_DICT_FRACT0 0x00008000 // Dictionary fraction in 1/32 of size.
#define FCI_DICT_FRACT1 0x00010000
#define FCI_DICT_FRACT2 0x00020000
#define FCI_DICT_FRACT3 0x00040000
#define FCI_DICT_FRACT4 0x00080000
#define FCI_RAR5_COMPAT 0x00100000 // RAR7 compression flags and RAR5 compression algorithm.
// Main header extra field values.
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
#define MHEXTRA_METADATA 0x02 // Archive metadata.
// Flags for MHEXTRA_LOCATOR.
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
// Flags for MHEXTRA_METADATA.
#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
// File and service header extra field values.
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
#define FHEXTRA_HASH 0x02 // File hash.
#define FHEXTRA_HTIME 0x03 // High precision file time.
#define FHEXTRA_VERSION 0x04 // File version information.
#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
// Hash type values for FHEXTRA_HASH.
#define FHEXTRA_HASH_BLAKE2 0x00
// Flags for FHEXTRA_HTIME.
#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision.
// Flags for FHEXTRA_CRYPT.
#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
// Flags for FHEXTRA_REDIR.
#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
// Flags for FHEXTRA_UOWNER.
#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
#endif
+111
View File
@@ -0,0 +1,111 @@
#include "rar.hpp"
DWORD WinNT()
{
static int dwPlatformId=-1;
static DWORD dwMajorVersion,dwMinorVersion;
if (dwPlatformId==-1)
{
OSVERSIONINFO WinVer;
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
GetVersionEx(&WinVer);
dwPlatformId=WinVer.dwPlatformId;
dwMajorVersion=WinVer.dwMajorVersion;
dwMinorVersion=WinVer.dwMinorVersion;
}
DWORD Result=0;
if (dwPlatformId==VER_PLATFORM_WIN32_NT)
Result=dwMajorVersion*0x100+dwMinorVersion;
return Result;
}
// Since Windows 10 development is stopped, we can assume that its build
// number never reaches 22000. So we do not need WMI anymore.
#if 0
// Replace it with documented Windows 11 check when available.
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
static bool WMI_IsWindows10()
{
IWbemLocator *pLoc = NULL;
HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
IID_IWbemLocator,(LPVOID *)&pLoc);
if (FAILED(hres))
return false;
IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,0,NULL,NULL,&pSvc);
if (FAILED(hres))
{
pLoc->Release();
return false;
}
hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,
RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);
if (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return false;
}
IEnumWbemClassObject *pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hres) || pEnumerator==NULL)
{
pSvc->Release();
pLoc->Release();
return false;
}
bool Win10=false;
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (pclsObj!=NULL && uReturn>0)
{
VARIANT vtProp;
pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL;
VariantClear(&vtProp);
pclsObj->Release();
}
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
return Win10;
}
#endif
// Replace it with actual check when available.
bool IsWindows11OrGreater()
{
static bool IsSet=false,IsWin11=false;
if (!IsSet)
{
OSVERSIONINFO WinVer;
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
GetVersionEx(&WinVer);
IsWin11=WinVer.dwMajorVersion>10 ||
WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000/* && !WMI_IsWindows10()*/;
IsSet=true;
}
return IsWin11;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef _RAR_ISNT_
#define _RAR_ISNT_
enum WINNT_VERSION {
WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
};
DWORD WinNT();
// Replace it with actual check when available.
bool IsWindows11OrGreater();
#endif
+201
View File
@@ -0,0 +1,201 @@
#include "rar.hpp"
/*
To enable, disable or check Large Memory pages manually:
- open "Local Security Policy" from "Start Menu";
- open "Lock Pages in Memory" in "Local Policies\User Rights Assignment";
- add or remove the user and sign out and sign in or restart Windows.
*/
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
#define ALLOW_LARGE_PAGES
#endif
LargePageAlloc::LargePageAlloc()
{
UseLargePages=false;
#ifdef ALLOW_LARGE_PAGES
PageSize=0;
#endif
}
void LargePageAlloc::AllowLargePages(bool Allow)
{
#ifdef ALLOW_LARGE_PAGES
if (Allow && PageSize==0)
{
HMODULE hKernel=GetModuleHandle(L"kernel32.dll");
if (hKernel!=nullptr)
{
typedef SIZE_T (*GETLARGEPAGEMINIMUM)();
GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum");
if (pGetLargePageMinimum!=nullptr)
PageSize=pGetLargePageMinimum();
}
if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME))
{
UseLargePages=false;
return;
}
}
UseLargePages=Allow;
#endif
}
bool LargePageAlloc::IsPrivilegeAssigned()
{
#ifdef ALLOW_LARGE_PAGES
return SetPrivilege(SE_LOCK_MEMORY_NAME);
#else
return true;
#endif
}
bool LargePageAlloc::AssignPrivilege()
{
#ifdef ALLOW_LARGE_PAGES
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// Get the required buffer size.
DWORD BufSize=0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize);
if (BufSize==0 || BufSize>1000000) // Sanity check for returned value.
{
CloseHandle(hToken);
return false;
}
TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize);
// Get the current user token information.
if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0)
{
CloseHandle(hToken);
return false;
}
// Get SID string for the current user.
LPWSTR ApiSidStr;
ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr);
// Convert SID to C++ string and release API based buffer.
std::wstring SidStr=ApiSidStr;
LocalFree(ApiSidStr);
CloseHandle(hToken);
if (IsUserAdmin())
AssignPrivilegeBySid(SidStr);
else
{
// Define here, so they survive until ShellExecuteEx call.
std::wstring ExeName=GetModuleFileStr();
std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr;
SHELLEXECUTEINFO shExecInfo{};
shExecInfo.cbSize = sizeof(shExecInfo);
shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode.
shExecInfo.lpVerb = L"runas";
shExecInfo.lpFile = ExeName.c_str();
shExecInfo.lpParameters = Param.c_str();
shExecInfo.nShow = SW_SHOWNORMAL;
BOOL Result=ShellExecuteEx(&shExecInfo);
}
#endif
return true;
}
bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid)
{
#ifdef ALLOW_LARGE_PAGES
LSA_HANDLE PolicyHandle;
LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it.
#ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR.
// We define STATUS_SUCCESS here instead of including ntstatus.h to avoid
// macro redefinition warnings. We tried UMDF_USING_NTSTATUS define
// and other workarounds, but it didn't help.
const uint STATUS_SUCCESS=0;
#endif
if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT|
POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS)
return false;
PSID UserSid;
ConvertStringSidToSid(Sid.c_str(),&UserSid);
LSA_UNICODE_STRING LsaString;
LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME;
// It must be in bytes, so multiple it to sizeof(wchar_t).
LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]);
LsaString.MaximumLength=LsaString.Length;
bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS;
LocalFree(UserSid);
LsaClose(PolicyHandle);
mprintf(St(MPrivilegeAssigned));
if (Ask(St(MYesNo)) == 1)
Shutdown(POWERMODE_RESTART);
return Success;
#else
return true;
#endif
}
bool LargePageAlloc::AssignConfirmation()
{
#ifdef ALLOW_LARGE_PAGES
mprintf(St(MLockInMemoryNeeded));
return Ask(St(MYesNo)) == 1;
#else
return false;
#endif
}
void* LargePageAlloc::new_large(size_t Size)
{
void *Allocated=nullptr;
#ifdef ALLOW_LARGE_PAGES
if (UseLargePages && Size>=PageSize)
{
// VirtualAlloc fails if allocation size isn't multiple of page size.
SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize;
Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE);
if (Allocated!=nullptr)
LargeAlloc.push_back(Allocated);
}
#endif
return Allocated;
}
bool LargePageAlloc::delete_large(void *Addr)
{
#ifdef ALLOW_LARGE_PAGES
if (Addr!=nullptr)
for (size_t I=0;I<LargeAlloc.size();I++)
if (LargeAlloc[I]==Addr)
{
LargeAlloc[I]=nullptr;
VirtualFree(Addr,0,MEM_RELEASE);
return true;
}
#endif
return false;
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef _RAR_LARGEPAGE_
#define _RAR_LARGEPAGE_
class LargePageAlloc
{
private:
static constexpr const wchar *LOCKMEM_SWITCH=L"isetup_privilege_lockmem";
void* new_large(size_t Size);
bool delete_large(void *Addr);
#ifdef _WIN_ALL
std::vector<void*> LargeAlloc;
SIZE_T PageSize;
#endif
bool UseLargePages;
public:
LargePageAlloc();
void AllowLargePages(bool Allow);
static bool IsPrivilegeAssigned();
static bool AssignPrivilege();
static bool AssignPrivilegeBySid(const std::wstring &Sid);
static bool AssignConfirmation();
static bool ProcessSwitch(CommandData *Cmd,const wchar *Switch)
{
if (Switch[0]==LOCKMEM_SWITCH[0])
{
size_t Length=wcslen(LOCKMEM_SWITCH);
if (wcsncmp(Switch,LOCKMEM_SWITCH,Length)==0)
{
LargePageAlloc::AssignPrivilegeBySid(Switch+Length);
return true;
}
}
return false;
}
template <class T> T* new_l(size_t Size,bool Clear=false)
{
T *Allocated=(T*)new_large(Size*sizeof(T));
if (Allocated==nullptr)
Allocated=Clear ? new T[Size]{} : new T[Size];
return Allocated;
}
template <class T> void delete_l(T *Addr)
{
if (!delete_large(Addr))
delete[] Addr;
}
};
#endif
+42
View File
@@ -0,0 +1,42 @@
****** ***** ****** UnRAR - free utility for RAR archives
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
****** ******* ****** License for use and distribution of
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
** ** ** ** ** ** FREE portable version
~~~~~~~~~~~~~~~~~~~~~
The source code of UnRAR utility is freeware. This means:
1. All copyrights to RAR and the utility UnRAR are exclusively
owned by the author - Alexander Roshal.
2. UnRAR source code may be used in any software to handle
RAR archives without limitations free of charge, but cannot be
used to develop RAR (WinRAR) compatible archiver and to
re-create RAR compression algorithm, which is proprietary.
Distribution of modified UnRAR source code in separate form
or as a part of other software is permitted, provided that
full text of this paragraph, starting from "UnRAR source code"
words, is included in license, or in documentation if license
is not available, and in source code comments of resulting package.
3. The UnRAR utility may be freely distributed. It is allowed
to distribute UnRAR inside of other software packages.
4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
OR MISUSING THIS SOFTWARE.
5. Installing and using the UnRAR utility signifies acceptance of
these terms and conditions of the license.
6. If you don't agree with terms of the license you must remove
UnRAR files from your storage devices and cease to use the
utility.
Thank you for your interest in RAR and UnRAR.
Alexander L. Roshal
+501
View File
@@ -0,0 +1,501 @@
#include "rar.hpp"
static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames);
static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
static void ListOldSubHeader(Archive &Arc);
static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
void ListArchive(CommandData *Cmd)
{
int64 SumPackSize=0,SumUnpSize=0;
uint ArcCount=0,SumFileCount=0;
bool Technical=(Cmd->Command[1]=='T');
bool ShowService=Technical && Cmd->Command[2]=='A';
bool Bare=(Cmd->Command[1]=='B');
bool Verbose=(Cmd->Command[0]=='V');
std::wstring ArcName;
while (Cmd->GetArcName(ArcName))
{
if (Cmd->ManualPassword)
Cmd->Password.Clean(); // Clean user entered password before processing next archive.
Archive Arc(Cmd);
if (!Arc.WOpen(ArcName))
continue;
bool FileMatched=true;
while (true)
{
int64 TotalPackSize=0,TotalUnpSize=0;
uint FileCount=0;
if (Arc.IsArchive(true))
{
bool TitleShown=false;
if (!Bare)
{
Arc.ViewComment();
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName.c_str());
mprintf(L"\n%s: ",St(MListDetails));
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 1.5":L"RAR 5");
mprintf(L"%s", Fmt);
if (Arc.Solid)
mprintf(L", %s", St(MListSolid));
if (Arc.SFXSize>0)
mprintf(L", %s", St(MListSFX));
if (Arc.Volume)
if (Arc.Format==RARFMT50)
{
// RAR 5.0 archives store the volume number in main header,
// so it is already available now.
mprintf(L", ");
mprintf(St(MVolumeNumber),Arc.VolNumber+1);
}
else
mprintf(L", %s", St(MListVolume));
if (Arc.Protected)
mprintf(L", %s", St(MListRR));
if (Arc.Locked)
mprintf(L", %s", St(MListLock));
if (Arc.Encrypted)
mprintf(L", %s", St(MListEncHead));
if (!Arc.MainHead.OrigName.empty())
mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str());
if (Arc.MainHead.OrigTime.IsSet())
{
wchar DateStr[50];
Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical);
mprintf(L"\n%s: %s",St(MOriginalTime),DateStr);
}
mprintf(L"\n");
}
wchar VolNumText[50];
*VolNumText=0;
while (Arc.ReadHeader()>0)
{
Wait(); // Allow quit listing with Ctrl+C.
HEADER_TYPE HeaderType=Arc.GetHeaderType();
if (HeaderType==HEAD_ENDARC)
{
#ifndef SFX_MODULE
// Only RAR 1.5 archives store the volume number in end record.
if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15)
swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1);
#endif
if (Technical && ShowService)
{
mprintf(L"\n%12ls: %ls",St(MListService),L"EOF");
if (*VolNumText!=0)
mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText);
mprintf(L"\n");
}
break;
}
switch(HeaderType)
{
case HEAD_FILE:
FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
if (FileMatched)
{
ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames);
if (!Arc.FileHead.SplitBefore)
{
TotalUnpSize+=Arc.FileHead.UnpSize;
FileCount++;
}
TotalPackSize+=Arc.FileHead.PackSize;
}
break;
case HEAD_SERVICE:
// For service blocks dependent on previous block, such as ACL
// or NTFS stream, we use "file matched" flag of host file.
// Independent blocks like RR are matched separately,
// so we can list them by their name. Also we match even
// dependent blocks separately if "vta -idn" are set. User may
// want to see service blocks only in this case.
if (!Arc.SubHead.SubBlock || Cmd->DisableNames)
FileMatched=Cmd->IsProcessFile(Arc.SubHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
if (FileMatched && !Bare)
{
// Here we set DisableNames parameter to true regardless of
// Cmd->DisableNames. If "vta -idn" are set together, user
// wants to see service blocks like RR only.
if (Technical && ShowService)
ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,false);
}
break;
}
Arc.SeekToNext();
}
if (!Bare && !Technical)
if (TitleShown)
{
wchar UnpSizeText[20];
itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText));
wchar PackSizeText[20];
itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText));
if (Verbose)
{
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
VolNumText,FileCount);
}
else
{
mprintf(L"\n----------- --------- ---------- ----- ----");
mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
}
SumFileCount+=FileCount;
SumUnpSize+=TotalUnpSize;
SumPackSize+=TotalPackSize;
mprintf(L"\n");
}
else
mprintf(St(MListNoFiles));
ArcCount++;
#ifndef NOVOLUME
if (Cmd->VolSize==VOLSIZE_AUTO && (Arc.FileHead.SplitAfter ||
Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
MergeArchive(Arc,NULL,false,Cmd->Command[0]))
Arc.Seek(0,SEEK_SET);
else
#endif
break;
}
else
{
if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
mprintf(St(MNotRAR),Arc.FileName.c_str());
break;
}
}
}
// Clean user entered password. Not really required, just for extra safety.
if (Cmd->ManualPassword)
Cmd->Password.Clean();
if (ArcCount>1 && !Bare && !Technical)
{
wchar UnpSizeText[20],PackSizeText[20];
itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText));
itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText));
if (Verbose)
mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText,
ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount);
else
mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount);
}
}
enum LISTCOL_TYPE {
LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR
};
void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames)
{
if (!TitleShown && !Technical && !Bare)
{
if (Verbose)
{
mprintf(L"\n%ls",St(MListTitleV));
if (!DisableNames)
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
}
else
{
mprintf(L"\n%ls",St(MListTitleL));
if (!DisableNames)
mprintf(L"\n----------- --------- ---------- ----- ----");
}
// Must be set even in DisableNames mode to suppress "0 files" output
// unless no files are matched.
TitleShown=true;
}
if (DisableNames)
return;
const wchar *Name=hd.FileName.c_str();
RARFORMAT Format=Arc.Format;
if (Bare)
{
mprintf(L"%s\n",Name);
return;
}
wchar UnpSizeText[30],PackSizeText[30];
if (hd.UnpSize==INT64NDF)
wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText));
else
itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
wchar AttrStr[30];
if (hd.HeaderType==HEAD_SERVICE)
swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.');
else
ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr));
wchar RatioStr[10];
if (hd.SplitBefore && hd.SplitAfter)
wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr));
else
if (hd.SplitBefore)
wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr));
else
if (hd.SplitAfter)
wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr));
else
swprintf(RatioStr,ASIZE(RatioStr),L"%u%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
wchar DateStr[50];
hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
if (Technical)
{
mprintf(L"\n%12s: %s",St(MListName),Name);
bool FileBlock=hd.HeaderType==HEAD_FILE;
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
{
mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
std::wstring StreamName=GetStreamNameNTFS(Arc);
mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName.c_str());
}
else
{
const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService);
if (hd.RedirType!=FSREDIR_NONE)
switch(hd.RedirType)
{
case FSREDIR_UNIXSYMLINK:
Type=St(MListUSymlink); break;
case FSREDIR_WINSYMLINK:
Type=St(MListWSymlink); break;
case FSREDIR_JUNCTION:
Type=St(MListJunction); break;
case FSREDIR_HARDLINK:
Type=St(MListHardlink); break;
case FSREDIR_FILECOPY:
Type=St(MListCopy); break;
}
mprintf(L"\n%12ls: %ls",St(MListType),Type);
if (hd.RedirType!=FSREDIR_NONE)
if (Format==RARFMT15)
{
std::string LinkTargetA;
if (Arc.FileHead.Encrypted)
{
// Link data are encrypted. We would need to ask for password
// and initialize decryption routine to display the link target.
LinkTargetA="*<-?->";
}
else
{
size_t DataSize=(size_t)Min(hd.PackSize,MAXPATHSIZE);
std::vector<char> Buf(DataSize+1);
Arc.Read(Buf.data(),DataSize);
Buf[DataSize] = 0;
LinkTargetA=Buf.data();
}
std::wstring LinkTarget;
CharToWide(LinkTargetA,LinkTarget);
mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget.c_str());
}
else
mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName.c_str());
}
if (!hd.Dir)
{
mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_RR))
{
// Display the original -rrN percent if available.
int RecoveryPercent=Arc.GetRecoveryPercent();
if (RecoveryPercent>0) // It can be -1 if failed to detect.
mprintf(L"\n%12ls: %u%%",L"RR%", RecoveryPercent);
}
}
bool WinTitles=false;
#ifdef _WIN_ALL
WinTitles=true;
#endif
if (hd.mtime.IsSet())
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr);
if (hd.ctime.IsSet())
{
hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr);
}
if (hd.atime.IsSet())
{
hd.atime.GetText(DateStr,ASIZE(DateStr),true);
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr);
}
mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
if (hd.FileHash.Type==HASH_CRC32)
mprintf(L"\n%12ls: %8.8X",
hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32",
hd.FileHash.CRC32);
if (hd.FileHash.Type==HASH_BLAKE2)
{
std::wstring BlakeStr;
BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,BlakeStr);
mprintf(L"\n%12ls: %ls",
hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
BlakeStr.c_str());
}
const wchar *HostOS=L"";
if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN)
HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix";
if (Format==RARFMT15)
{
static const wchar *RarOS[]={
L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L""
};
if (hd.HostOS<ASIZE(RarOS))
HostOS=RarOS[hd.HostOS];
}
if (*HostOS!=0)
mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
std::wstring WinSize;
if (!hd.Dir)
if (hd.WinSize%1073741824==0)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"g";
else
if (hd.WinSize%1048576==0)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"m";
else
if (hd.WinSize>=1024)
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"k";
else
WinSize=L" -md=?";
mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo),
Format==RARFMT15 ? L"1.5":L"5.0",
hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,WinSize.c_str());
if (hd.Solid || hd.Encrypted)
{
mprintf(L"\n%12ls: ",St(MListFlags));
if (hd.Solid)
mprintf(L"%ls ",St(MListSolid));
if (hd.Encrypted)
mprintf(L"%ls ",St(MListEnc));
}
if (hd.Version)
{
uint Version=ParseVersionFileName(hd.FileName,false);
if (Version!=0)
mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
}
if (hd.UnixOwnerSet)
{
mprintf(L"\n%12ls: ",L"Unix owner");
if (*hd.UnixOwnerName!=0)
mprintf(L"%ls",GetWide(hd.UnixOwnerName).c_str());
else
if (hd.UnixOwnerNumeric)
mprintf(L"#%d",hd.UnixOwnerID);
mprintf(L":");
if (*hd.UnixGroupName!=0)
mprintf(L"%ls",GetWide(hd.UnixGroupName).c_str());
else
if (hd.UnixGroupNumeric)
mprintf(L"#%d",hd.UnixGroupID);
}
mprintf(L"\n");
return;
}
mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
if (Verbose)
mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
mprintf(L" %ls ",DateStr);
if (Verbose)
{
if (hd.FileHash.Type==HASH_CRC32)
mprintf(L"%8.8X ",hd.FileHash.CRC32);
else
if (hd.FileHash.Type==HASH_BLAKE2)
{
byte *S=hd.FileHash.Digest;
mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
}
else
mprintf(hd.Dir ? L" ":L"???????? "); // Missing checksum is ok for folder, not for file.
}
mprintf(L"%ls",Name);
}
void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
{
switch(HostType)
{
case HSYS_WINDOWS:
swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c",
(A & 0x2000)!=0 ? 'I' : '.', // Not content indexed.
(A & 0x0800)!=0 ? 'C' : '.', // Compressed.
(A & 0x0020)!=0 ? 'A' : '.', // Archive.
(A & 0x0010)!=0 ? 'D' : '.', // Directory.
(A & 0x0004)!=0 ? 'S' : '.', // System.
(A & 0x0002)!=0 ? 'H' : '.', // Hidden.
(A & 0x0001)!=0 ? 'R' : '.'); // Read-only.
break;
case HSYS_UNIX:
switch (A & 0xF000)
{
case 0x4000:
AttrStr[0]='d';
break;
case 0xA000:
AttrStr[0]='l';
break;
default:
AttrStr[0]='-';
break;
}
swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c",
(A & 0x0100) ? 'r' : '-',
(A & 0x0080) ? 'w' : '-',
(A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'),
(A & 0x0020) ? 'r' : '-',
(A & 0x0010) ? 'w' : '-',
(A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'),
(A & 0x0004) ? 'r' : '-',
(A & 0x0002) ? 'w' : '-',
(A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
break;
case HSYS_UNKNOWN:
wcsncpyz(AttrStr,L"?",AttrSize);
break;
}
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef _RAR_LIST_
#define _RAR_LIST_
void ListArchive(CommandData *Cmd);
#endif
+409
View File
@@ -0,0 +1,409 @@
#define MYesNo L"_Yes_No"
#define MYesNoAll L"_Yes_No_All"
#define MYesNoAllQ L"_Yes_No_All_nEver_Quit"
#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit"
#define MContinueQuit L"_Continue_Quit"
#define MRetryAbort L"_Retry_Abort"
#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit"
#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d"
#define MRegTo L"\nRegistered to %s\n"
#define MShare L"\nTrial version Type 'rar -?' for help\n"
#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n"
#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n"
#define MBeta L"beta"
#define Mx86 L"x86"
#define Mx64 L"x64"
#define MMonthJan L"January"
#define MMonthFeb L"February"
#define MMonthMar L"March"
#define MMonthApr L"April"
#define MMonthMay L"May"
#define MMonthJun L"June"
#define MMonthJul L"July"
#define MMonthAug L"August"
#define MMonthSep L"September"
#define MMonthOct L"October"
#define MMonthNov L"November"
#define MMonthDec L"December"
#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>"
#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>"
#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>"
#define MCHelpCmd L"\n\n<Commands>"
#define MCHelpCmdA L"\n a Add files to archive"
#define MCHelpCmdC L"\n c Add archive comment"
#define MCHelpCmdCH L"\n ch Change archive parameters"
#define MCHelpCmdCW L"\n cw Write archive comment to file"
#define MCHelpCmdD L"\n d Delete files from archive"
#define MCHelpCmdE L"\n e Extract files without archived paths"
#define MCHelpCmdF L"\n f Freshen files in archive"
#define MCHelpCmdI L"\n i[par]=<str> Find string in archives"
#define MCHelpCmdK L"\n k Lock archive"
#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]"
#define MCHelpCmdM L"\n m[f] Move to archive [files only]"
#define MCHelpCmdP L"\n p Print file to stdout"
#define MCHelpCmdR L"\n r Repair archive"
#define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
#define MCHelpCmdRN L"\n rn Rename archived files"
#define MCHelpCmdRR L"\n rr[N] Add data recovery record"
#define MCHelpCmdRV L"\n rv[N] Create recovery volumes"
#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
#define MCHelpCmdT L"\n t Test archive files"
#define MCHelpCmdU L"\n u Update files in archive"
#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]"
#define MCHelpCmdX L"\n x Extract files with full path"
#define MCHelpSw L"\n\n<Switches>"
#define MCHelpSwm L"\n - Stop switches scanning"
#define MCHelpSwAT L"\n @[+] Disable [enable] file lists"
#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction"
#define MCHelpSwAD L"\n ad[1,2] Alternate destination path"
#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
#define MCHelpSwAI L"\n ai Ignore file attributes"
#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]"
#define MCHelpSwAO L"\n ao Add files with Archive attribute set"
#define MCHelpSwAP L"\n ap<path> Set path inside archive"
#define MCHelpSwAS L"\n as Synchronize archive contents"
#define MCHelpSwCm L"\n c- Disable comments show"
#define MCHelpSwCFGm L"\n cfg- Disable read configuration"
#define MCHelpSwCL L"\n cl Convert names to lower case"
#define MCHelpSwCU L"\n cu Convert names to upper case"
#define MCHelpSwDF L"\n df Delete files after archiving"
#define MCHelpSwDH L"\n dh Open shared files"
#define MCHelpSwDR L"\n dr Delete files to Recycle Bin"
#define MCHelpSwDS L"\n ds Disable name sort for solid archive"
#define MCHelpSwDW L"\n dw Wipe files after archiving"
#define MCHelpSwEa L"\n e[+]<attr> Set file exclude and include attributes"
#define MCHelpSwED L"\n ed Do not add empty directories"
#define MCHelpSwEP L"\n ep Exclude paths from names"
#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names"
#define MCHelpSwEP2 L"\n ep2 Expand paths to full"
#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter"
#define MCHelpSwEP4 L"\n ep4<path> Exclude the path prefix from names"
#define MCHelpSwF L"\n f Freshen files"
#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers"
#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum"
#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages"
#define MCHelpSwIEML L"\n ieml[addr] Send archive by email"
#define MCHelpSwIERR L"\n ierr Send all messages to stderr"
#define MCHelpSwILOG L"\n ilog[name] Log errors to file"
#define MCHelpSwINUL L"\n inul Disable all messages"
#define MCHelpSwIOFF L"\n ioff[n] Turn PC off after completing an operation"
#define MCHelpSwISND L"\n isnd[-] Control notification sounds"
#define MCHelpSwIVER L"\n iver Display the version number"
#define MCHelpSwK L"\n k Lock archive"
#define MCHelpSwKB L"\n kb Keep broken extracted files"
#define MCHelpSwLog L"\n log[f][=name] Write names to log file"
#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)"
#define MCHelpSwMC L"\n mc<par> Set advanced compression parameters"
#define MCHelpSwMD L"\n md[x]<n>[kmg] Dictionary size in KB, MB or GB"
#define MCHelpSwME L"\n me[par] Set encryption parameters"
#define MCHelpSwMLP L"\n mlp Use large memory pages"
#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store"
#define MCHelpSwMT L"\n mt<threads> Set the number of threads"
#define MCHelpSwN L"\n n<file> Additionally filter included files"
#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin"
#define MCHelpSwNal L"\n n@<list> Read additional filter masks from list file"
#define MCHelpSwO L"\n o[+|-] Set the overwrite mode"
#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute"
#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file"
#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references"
#define MCHelpSwOL L"\n ol[a,-] Process symbolic links as the link [absolute paths, skip]"
#define MCHelpSwOM L"\n om[-|1][=lst] Propagate Mark of the Web"
#define MCHelpSwONI L"\n oni Allow potentially incompatible names"
#define MCHelpSwOP L"\n op<path> Set the output path for extracted files"
#define MCHelpSwOR L"\n or Rename files automatically"
#define MCHelpSwOS L"\n os Save NTFS streams"
#define MCHelpSwOW L"\n ow Save or restore file owner and group"
#define MCHelpSwP L"\n p[password] Set password"
#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]"
#define MCHelpSwR L"\n r Recurse subdirectories"
#define MCHelpSwRm L"\n r- Disable recursion"
#define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only"
#define MCHelpSwRI L"\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms"
#define MCHelpSwRR L"\n rr[N] Add data recovery record"
#define MCHelpSwRV L"\n rv[N] Create recovery volumes"
#define MCHelpSwS L"\n s[=<par>] Create solid archive"
#define MCHelpSwSC L"\n sc<chr>[obj] Specify the character set"
#define MCHelpSwSFX L"\n sfx[name] Create SFX archive"
#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)"
#define MCHelpSwSL L"\n sl<size>[u] Process files with size less than specified"
#define MCHelpSwSM L"\n sm<size>[u] Process files with size more than specified"
#define MCHelpSwT L"\n t Test files after archiving"
#define MCHelpSwTK L"\n tk[<date>] Keep the original or set the specified archive time"
#define MCHelpSwTL L"\n tl Set archive time to latest file"
#define MCHelpSwTN L"\n tn[mcao]<t> Process files newer than <t> time"
#define MCHelpSwTO L"\n to[mcao]<t> Process files older than <t> time"
#define MCHelpSwTA L"\n ta[mcao]<d> Process files modified after <d> YYYYMMDDHHMMSS date"
#define MCHelpSwTB L"\n tb[mcao]<d> Process files modified before <d> YYYYMMDDHHMMSS date"
#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)"
#define MCHelpSwU L"\n u Update files"
#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes"
#define MCHelpSwVUnr L"\n v List all volumes"
#define MCHelpSwVn L"\n v<size>[u] Create volumes with size in [bBkKmMgGtT] units"
#define MCHelpSwVD L"\n vd Erase disk contents before creating volume"
#define MCHelpSwVER L"\n ver[n] File version control"
#define MCHelpSwVP L"\n vp Pause before each volume"
#define MCHelpSwW L"\n w<path> Assign work directory"
#define MCHelpSwX L"\n x<file> Exclude specified file"
#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin"
#define MCHelpSwXal L"\n x@<list> Exclude files listed in specified list file"
#define MCHelpSwY L"\n y Assume Yes on all queries"
#define MCHelpSwZ L"\n z[file] Read archive comment from file"
#define MBadArc L"\nERROR: Bad archive %s\n"
#define MAskPsw L"Enter password (will not be echoed)"
#define MAskPswFor L"\nEnter password (will not be echoed) for %s: "
#define MReAskPsw L"\nReenter password: "
#define MNotMatchPsw L"\nERROR: Passwords do not match\n"
#define MErrWrite L"Write error in the file %s"
#define MErrRead L"Read error in the file %s"
#define MErrSeek L"Seek error in the file %s"
#define MErrFClose L"Cannot close the file %s"
#define MErrOutMem L"Not enough memory"
#define MErrBrokenArc L"Corrupt archive - use 'Repair' command"
#define MProgAborted L"Program aborted"
#define MErrRename L"\nCannot rename %s to %s"
#define MAbsNextVol L"\nCannot find volume %s"
#define MBreak L"\nUser break\n"
#define MAskCreatVol L"\nCreate next volume?"
#define MAskNextDisk L"\nDisk full. Insert next"
#define MCreatVol L"\n\nCreating %sarchive %s\n"
#define MAskNextVol L"\nInsert disk with %s"
#define MTestVol L"\n\nTesting archive %s\n"
#define MExtrVol L"\n\nExtracting from %s\n"
#define MConverting L"\nConverting %s"
#define MCvtToSFX L"\nConvert archives to SFX"
#define MCvtFromSFX L"\nRemoving SFX module"
#define MNotSFX L"\n%s is not SFX archive"
#define MNotRAR L"\n%s is not RAR archive"
#define MNotFirstVol L"\n%s is not the first volume"
#define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format"
#define MCannotCreate L"\nCannot create %s"
#define MCannotOpen L"\nCannot open %s"
#define MUnknownMeth L"\nUnknown method in %s"
#define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version."
#define MOk L" OK"
#define MDone L"\nDone"
#define MLockingArc L"\nLocking archive"
#define MNotMdfOld L"\n\nERROR: Cannot modify old format archive"
#define MNotMdfLock L"\n\nERROR: Locked archive"
#define MNotMdfVol L"\n\nERROR: Cannot modify volume"
#define MPackAskReg L"\nEvaluation copy. Please register.\n"
#define MCreateArchive L"\nCreating %sarchive %s\n"
#define MUpdateArchive L"\nUpdating %sarchive %s\n"
#define MAddSolid L"solid "
#define MAddFile L"\nAdding %-58s "
#define MUpdFile L"\nUpdating %-58s "
#define MAddPoints L"\n... %-58s "
#define MMoveDelFiles L"\n\nDeleting files %s..."
#define MMoveDelDirs L"and directories"
#define MMoveDelFile L"\nDeleting %-30s"
#define MMoveDeleted L" deleted"
#define MMoveNotDeleted L" NOT DELETED"
#define MClearAttrib L"\n\nClearing attributes..."
#define MMoveDelDir L"\nDeleting directory %-30s"
#define MWarErrFOpen L"\nWARNING: Cannot open %d %s"
#define MErrOpenFiles L"files"
#define MErrOpenFile L"file"
#define MAddNoFiles L"\nWARNING: No files"
#define MMdfEncrSol L"\n%s: encrypted"
#define MAddAnalyze L"\nAnalyzing archived files: "
#define MRepacking L"\nRepacking archived files: "
#define MCRCFailed L"\n%-20s - checksum error"
#define MExtrTest L"\nTesting archive %s\n"
#define MExtracting L"\nExtracting from %s\n"
#define MUseCurPsw L"\n%s - use current password?"
#define MCreatDir L"\nCreating %-56s"
#define MExtrSkipFile L"\nSkipping %-56s"
#define MExtrTestFile L"\nTesting %-56s"
#define MExtrFile L"\nExtracting %-56s"
#define MExtrPoints L"\n... %-56s"
#define MExtrErrMkDir L"\nCannot create directory %s"
#define MExtrPrinting L"\n------ Printing %s\n\n"
#define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password."
#define MExtrNoFiles L"\nNo files to extract"
#define MExtrAllOk L"\nAll OK"
#define MExtrTotalErr L"\nTotal errors: %ld"
#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n"
#define MAskOverwrite L"\nOverwrite %s?"
#define MAskNewName L"\nEnter new name: "
#define MHeaderBroken L"\nCorrupt header is found"
#define MMainHeaderBroken L"\nMain archive header is corrupt"
#define MLogFileHead L"\n%s - the file header is corrupt"
#define MLogProtectHead L"The data recovery header is corrupt"
#define MArcComment L"\nArchive comment"
#define MReadStdinCmt L"\nReading comment from stdin\n"
#define MReadCommFrom L"\nReading comment from %s"
#define MDelComment L"\nDeleting a comment from %s"
#define MAddComment L"\nAdding a comment to %s"
#define MLogCommBrk L"\nThe archive comment is corrupt"
#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:"
#define MWriteCommTo L"\nWrite comment to %s"
#define MCommNotPres L"\nComment is not present"
#define MDelFrom L"\nDeleting from %s"
#define MDeleting L"\nDeleting %s"
#define MEraseArc L"\nErasing empty archive %s"
#define MNoDelFiles L"\nNo files to delete"
#define MLogTitle L"-------- %2d %s %d, archive %s"
#define MPathTooLong L"\nERROR: Path too long\n"
#define MListArchive L"Archive"
#define MListDetails L"Details"
#define MListSolid L"solid"
#define MListSFX L"SFX"
#define MListVolume L"volume"
#define MListRR L"recovery record"
#define MListLock L"lock"
#define MListEnc L"encrypted"
#define MListEncHead L"encrypted headers"
#define MListTitleL L" Attributes Size Date Time Name"
#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name"
#define MListName L"Name"
#define MListType L"Type"
#define MListFile L"File"
#define MListDir L"Directory"
#define MListUSymlink L"Unix symbolic link"
#define MListWSymlink L"Windows symbolic link"
#define MListJunction L"NTFS junction point"
#define MListHardlink L"Hard link"
#define MListCopy L"File reference"
#define MListStream L"NTFS alternate data stream"
#define MListTarget L"Target"
#define MListSize L"Size"
#define MListPacked L"Packed size"
#define MListRatio L"Ratio"
#define MListMtime L"mtime"
#define MListCtime L"ctime"
#define MListAtime L"atime"
#define MListModified L"Modified"
#define MListCreated L"Created"
#define MListAccessed L"Accessed"
#define MListAttr L"Attributes"
#define MListFlags L"Flags"
#define MListCompInfo L"Compression"
#define MListHostOS L"Host OS"
#define MListFileVer L"File version"
#define MListService L"Service"
#define MListNTACLHead L"\n NTFS security data"
#define MListStrmHead L"\n NTFS stream: %s"
#define MListUnkHead L"\n Unknown subheader type: 0x%04x"
#define MYes L"Yes"
#define MNo L"No"
#define MListNoFiles L" 0 files\n"
#define MRprReconstr L"\nReconstructing %s"
#define MRprBuild L"\nBuilding %s"
#define MRprOldFormat L"\nCannot repair archive with old format"
#define MRprFind L"\nFound %s"
#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid?"
#define MRprNoFiles L"\nNo files found"
#define MLogUnexpEOF L"\nUnexpected end of archive"
#define MRepAskReconst L"\nReconstruct archive structure?"
#define MRRSearch L"\nSearching for recovery record"
#define MAnalyzeFileData L"\nAnalyzing file data"
#define MRecRNotFound L"\nData recovery record not found"
#define MRecRFound L"\nData recovery record found"
#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged"
#define MRecCorrected L" - data recovered"
#define MRecFailed L" - cannot recover data"
#define MAddRecRec L"\nAdding the data recovery record"
#define MEraseForVolume L"\n\nErasing contents of drive %c:\n"
#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n"
#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n"
#define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n"
#define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n"
#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n"
#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s"
#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists"
#define MAskRetryCreate L"\nCannot create %s. Retry?"
#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s"
#define MFileRO L"\n%s is read-only"
#define MACLGetError L"\nWARNING: Cannot get %s security data\n"
#define MACLSetError L"\nWARNING: Cannot set %s security data\n"
#define MACLBroken L"\nERROR: %s security data are corrupt\n"
#define MACLUnknown L"\nWARNING: Unknown format of %s security data\n"
#define MStreamBroken L"\nERROR: %s stream data are corrupt\n"
#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n"
#define MInvalidName L"\nERROR: Invalid file name %s"
#define MProcessArc L"\nProcessing archive %s"
#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name"
#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s"
#define MUnknownOption L"\nERROR: Unknown option: %s"
#define MSwSyntaxError L"\nERROR: '-' is expected in the beginning of: %s"
#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored"
#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored"
#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block"
#define MScanError L"\nCannot read contents of %s"
#define MNotVolume L"\n%s is not volume"
#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets"
#define MRecVolMissing L"\n%d volumes missing"
#define MRecVolFound L"\n%d recovery volumes found"
#define MRecVolAllExist L"\nNothing to reconstruct"
#define MRecVolCannotFix L"\nReconstruction impossible"
#define MReconstructing L"\nReconstructing..."
#define MCreating L"\nCreating %s"
#define MRenaming L"\nRenaming %s to %s"
#define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB"
#define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files"
#define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s"
#define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives"
#define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive"
#define MCannotEmail L"\nCannot email the file %s"
#define MCopyrightS L"\nRAR SFX archive"
#define MSHelpCmd L"\n\n<Commands>"
#define MSHelpCmdE L"\n -x Extract from archive (default)"
#define MSHelpCmdT L"\n -t Test archive files"
#define MSHelpCmdV L"\n -v Verbosely list contents of archive"
#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d"
#define MVolumeNumber L"volume %d"
#define MCannotDelete L"\nCannot delete %s"
#define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin"
#define MCalcCRC L"\nCalculating the checksum"
#define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB."
#define MCalcCRCAllVol L"\nCalculating checksums of all volumes."
#define MNotEnoughDisk L"\nERROR: Not enough disk space for %s."
#define MNewerRAR L"\nYou may need a newer version of RAR."
#define MUnkEncMethod L"\nUnknown encryption method in %s"
#define MWrongPassword L"\nThe specified password is incorrect."
#define MWrongFilePassword L"\nIncorrect password for %s"
#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x"
#define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated"
#define MRRDamaged L"\nRecovery record is corrupt."
#define MTestingRR L"\nTesting the recovery record"
#define MFailed L"Failed"
#define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format."
#define MSearchDupFiles L"\nSearching for identical files"
#define MNumFound L"%d found."
#define MUnknownExtra L"\nUnknown extra field in %s."
#define MCorruptExtra L"\nCorrupt %s extra field in %s."
#define MCopyError L"\nCannot copy %s to %s."
#define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries."
#define MCopyingData L"\nCopying data"
#define MErrCreateLnkS L"\nCannot create symbolic link %s"
#define MErrCreateLnkH L"\nCannot create hard link %s"
#define MErrLnkTarget L"\nYou need to unpack the link target first"
#define MNeedAdmin L"\nYou may need to run RAR as administrator"
#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB."
#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary."
#define MExtrDictOutMem L"\nNot enough memory to unpack the archive with %u GB compression dictionary."
#define MSuggest64bit L"\n64-bit RAR version is necessary."
#define MOpenErrAtime L"\nYou may need to remove -tsp switch or run RAR as administrator to open this file."
#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort."
#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n"
#define MErrReadCount L"\n%u files are archived incompletely because of read errors."
#define MDirNameExists L"\nDirectory with such name already exists"
#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin"
#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated."
#define MAdjustValue L"\nAdjusting %s value to %s."
#define MOpFailed L"\nOperation failed"
#define MSkipEncArc L"\nSkipping the encrypted archive %s"
#define MOrigName L"Original name"
#define MOriginalTime L"Original time"
#define MFileRenamed L"\n%s is renamed to %s"
#define MDictNotAllowed L"\n%u GB dictionary exceeds %u GB limit and needs more than %u GB memory to unpack."
#define MDictExtrAnyway L"\nUse -md%ug or -mdx%ug switches to extract anyway."
#define MDictComprLimit L"\n%u GB dictionary exceeds %u GB limit and not allowed when compressing data."
#define MNeedSFX64 L"\n64-bit self-extracting module is necessary for %u GB compression dictionary."
#define MSkipUnsafeLink L"\nSkipping the potentially unsafe %s -> %s link. For archives from a trustworthy source use -ola to extract it anyway."
#define MTruncService L"\nTruncated at the service block: %s"
#define MHeaderQO L"quick open information"
#define MHeaderRR L"recovery record"
#define MLockInMemoryNeeded L"-mlp switch requires ""Lock pages in memory"" privilege. Do you wish to assign it to the current user account?"
#define MPrivilegeAssigned L"User privilege has been successfully assigned and will be activated after Windows restart. Restart now?"
+36
View File
@@ -0,0 +1,36 @@
#include "rar.hpp"
void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet)
{
}
void CloseLogOptions()
{
}
#ifndef SILENT
void Log(const wchar *ArcName,const wchar *fmt,...)
{
// Preserve the error code for possible following system error message.
int Code=ErrHandler.GetSystemErrorCode();
uiAlarm(UIALARM_ERROR);
va_list arglist;
va_start(arglist,fmt);
std::wstring s=vwstrprintf(fmt,arglist);
ReplaceEsc(s);
va_end(arglist);
eprintf(L"%ls",s.c_str());
ErrHandler.SetSystemErrorCode(Code);
}
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef _RAR_LOG_
#define _RAR_LOG_
void InitLogOptions(const std::wstring &LogFileName,RAR_CHARSET CSet);
void CloseLogOptions();
#ifdef SILENT
inline void Log(const wchar *ArcName,const wchar *fmt,...) {}
#else
void Log(const wchar *ArcName,const wchar *fmt,...);
#endif
#endif
+79
View File
@@ -0,0 +1,79 @@
#
# Makefile for UNIX - unrar
# Linux using GCC
# 2024.08.19: -march=native isn't recognized on some platforms such as RISCV64.
# Thus we removed it. Clang ARM users can add -march=armv8-a+crypto to enable
# ARM NEON crypto.
CXX=c++
CXXFLAGS=-O2 -std=c++11 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else
LIBFLAGS=-fPIC
DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
STRIP=strip
AR=ar
LDFLAGS=-pthread
DESTDIR=/usr
##########################
COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES)
LINK=$(CXX)
WHAT=UNRAR
UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o
LIB_OBJ=filestr.o scantree.o dll.o qopen.o
OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \
archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \
resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \
rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \
list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o largepage.o
.cpp.o:
$(COMPILE) -D$(WHAT) -c $<
all: unrar
install: install-unrar
uninstall: uninstall-unrar
clean:
@rm -f *.bak *~
@rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ)
@rm -f unrar libunrar.*
# We removed 'clean' from dependencies, because it prevented parallel
# 'make -Jn' builds.
unrar: $(OBJECTS) $(UNRAR_OBJ)
@rm -f unrar
$(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
$(STRIP) unrar
sfx: WHAT=SFX_MODULE
sfx: $(OBJECTS)
@rm -f default.sfx
$(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS)
$(STRIP) default.sfx
lib: WHAT=RARDLL
lib: CXXFLAGS+=$(LIBFLAGS)
lib: $(OBJECTS) $(LIB_OBJ)
@rm -f libunrar.*
$(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
$(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)
install-unrar:
install -D unrar $(DESTDIR)/bin/unrar
uninstall-unrar:
rm -f $(DESTDIR)/bin/unrar
install-lib:
install libunrar.so $(DESTDIR)/lib
install libunrar.a $(DESTDIR)/lib
uninstall-lib:
rm -f $(DESTDIR)/lib/libunrar.so
+167
View File
@@ -0,0 +1,167 @@
#include "rar.hpp"
static bool match(const wchar *pattern,const wchar *string,bool ForceCase);
static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase);
static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase);
static bool IsWildcard(const wchar *Str,size_t CheckSize);
inline uint touppercw(uint ch,bool ForceCase)
{
if (ForceCase)
return ch;
#if defined(_UNIX)
return ch;
#else
return toupperw(ch);
#endif
}
bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode)
{
bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0;
CmpMode&=MATCH_MODEMASK;
wchar *Name1=PointToName(Wildcard);
wchar *Name2=PointToName(Name);
if (CmpMode!=MATCH_NAMES)
{
size_t WildLength=wcslen(Wildcard);
if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD &&
mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0)
{
// For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD,
// "path1" mask must match "path1\path2\filename.ext" and "path1" names.
wchar NextCh=Name[WildLength];
if (NextCh==L'\\' || NextCh==L'/' || NextCh==0)
return true;
}
// Nothing more to compare for MATCH_SUBPATHONLY.
if (CmpMode==MATCH_SUBPATHONLY)
return false;
// 2023.08.29: We tried std::wstring Path1 and Path2 here, but performance
// impact for O(n^2) complexity loop in CmdExtract::AnalyzeArchive()
// was rather noticeable, 1.7s instead of 0.9s when extracting ~300 files
// with @listfile from archive with ~7000 files.
// This function can be invoked from other O(n^2) loops. So for now
// we prefer to avoid wstring and use pointers and path sizes here.
// Another option could be using std::wstring_view.
size_t Path1Size=Name1-Wildcard;
size_t Path2Size=Name2-Name;
if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
(Path1Size!=Path2Size ||
mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0))
return false;
if (CmpMode==MATCH_ALLWILD)
return match(Wildcard,Name,ForceCase);
if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH)
if (IsWildcard(Wildcard,Path1Size))
return match(Wildcard,Name,ForceCase);
else
if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard))
{
if (Path1Size>0 && mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)
return false;
}
else
if (Path1Size!=Path2Size || mwcsnicompc(Wildcard,Name,Path1Size,ForceCase)!=0)
return false;
}
if (CmpMode==MATCH_EXACT)
return mwcsicompc(Name1,Name2,ForceCase)==0;
return match(Name1,Name2,ForceCase);
}
bool match(const wchar *pattern,const wchar *string,bool ForceCase)
{
for (;; ++string)
{
wchar stringc=touppercw(*string,ForceCase);
wchar patternc=touppercw(*pattern++,ForceCase);
switch (patternc)
{
case 0:
return stringc==0;
case '?':
if (stringc == 0)
return false;
break;
case '*':
if (*pattern==0)
return true;
if (*pattern=='.')
{
if (pattern[1]=='*' && pattern[2]==0)
return true;
const wchar *dot=wcschr(string,'.');
if (pattern[1]==0)
return (dot==NULL || dot[1]==0);
if (dot!=NULL)
{
string=dot;
if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL)
return mwcsicompc(pattern+1,string+1,ForceCase)==0;
}
}
while (*string)
if (match(pattern,string++,ForceCase))
return true;
return false;
default:
if (patternc != stringc)
{
// Allow "name." mask match "name" and "name.\" match "name\".
if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.'))
return match(pattern,string,ForceCase);
else
return false;
}
break;
}
}
}
int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase)
{
if (ForceCase)
return wcscmp(Str1,Str2);
return wcsicompc(Str1,Str2);
}
int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase)
{
if (ForceCase)
return wcsncmp(Str1,Str2,N);
#if defined(_UNIX)
return wcsncmp(Str1,Str2,N);
#else
return wcsnicomp(Str1,Str2,N);
#endif
}
bool IsWildcard(const wchar *Str,size_t CheckSize)
{
size_t CheckPos=0;
#ifdef _WIN_ALL
// Not treat the special NTFS \\?\d: path prefix as a wildcard.
if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\')
CheckPos+=4;
#endif
for (size_t I=CheckPos;I<CheckSize && Str[I]!=0;I++)
if (Str[I]=='*' || Str[I]=='?')
return true;
return false;
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef _RAR_MATCH_
#define _RAR_MATCH_
enum {
MATCH_NAMES, // Paths are ignored.
// Compares names only using wildcards.
MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard
// must be present in the beginning of file path.
// For example, "c:\path1\*" or "c:\path1" will match
// "c:\path1\path2\file".
// Names are not compared.
MATCH_EXACT, // Paths must match exactly.
// Names must match exactly.
MATCH_ALLWILD, // Paths and names are compared using wildcards.
// Unlike MATCH_SUBPATH, paths do not match subdirs
// unless a wildcard tells so.
MATCH_EXACTPATH, // Paths must match exactly.
// Names are compared using wildcards.
MATCH_SUBPATH, // Names must be the same, but path in mask is allowed
// to be only a part of name path. In other words,
// we match all files matching the file mask
// in current folder and subfolders.
MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains
// wildcards and as MATCH_EXACTPATH otherwise.
};
#define MATCH_MODEMASK 0x0000ffff
#define MATCH_FORCECASESENSITIVE 0x80000000
bool CmpName(const wchar *Wildcard,const wchar *Name,uint CmpMode);
inline bool CmpName(const std::wstring &Wildcard,const std::wstring &Name,uint CmpMode)
{
return CmpName(Wildcard.c_str(),Name.c_str(),CmpMode);
}
#endif
+641
View File
@@ -0,0 +1,641 @@
/****************************************************************************
* This file is part of PPMd project *
* Written and distributed to public domain by Dmitry Shkarin 1997, *
* 1999-2000 *
* Contents: model description and encoding/decoding routines *
****************************************************************************/
static const int MAX_O=64; /* maximum allowed model order */
const uint TOP=1 << 24, BOT=1 << 15;
template <class T>
inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; }
inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats,
RARPPM_STATE& FirstState)
{
RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext();
if ( pc )
{
pc->NumStats=1;
pc->OneState=FirstState;
pc->Suffix=this;
pStats->Successor=pc;
}
return pc;
}
ModelPPM::ModelPPM()
{
MinContext=NULL;
MaxContext=NULL;
MedContext=NULL;
}
void ModelPPM::RestartModelRare()
{
int i, k, m;
memset(CharMask,0,sizeof(CharMask));
SubAlloc.InitSubAllocator();
InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1;
MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext();
if (MinContext == NULL)
throw std::bad_alloc();
MinContext->Suffix=NULL;
OrderFall=MaxOrder;
MinContext->U.SummFreq=(MinContext->NumStats=256)+1;
FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2);
if (FoundState == NULL)
throw std::bad_alloc();
for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++)
{
MinContext->U.Stats[i].Symbol=i;
MinContext->U.Stats[i].Freq=1;
MinContext->U.Stats[i].Successor=NULL;
}
static const ushort InitBinEsc[]={
0x3CDD,0x1F3F,0x59BF,0x48F3,0x64A1,0x5ABC,0x6632,0x6051
};
for (i=0;i < 128;i++)
for (k=0;k < 8;k++)
for (m=0;m < 64;m += 8)
BinSumm[i][k+m]=BIN_SCALE-InitBinEsc[k]/(i+2);
for (i=0;i < 25;i++)
for (k=0;k < 16;k++)
SEE2Cont[i][k].init(5*i+10);
}
void ModelPPM::StartModelRare(int MaxOrder)
{
int i, k, m ,Step;
EscCount=1;
/*
if (MaxOrder < 2)
{
memset(CharMask,0,sizeof(CharMask));
OrderFall=ModelPPM::MaxOrder;
MinContext=MaxContext;
while (MinContext->Suffix != NULL)
{
MinContext=MinContext->Suffix;
OrderFall--;
}
FoundState=MinContext->U.Stats;
MinContext=MaxContext;
}
else
*/
{
ModelPPM::MaxOrder=MaxOrder;
RestartModelRare();
NS2BSIndx[0]=2*0;
NS2BSIndx[1]=2*1;
memset(NS2BSIndx+2,2*2,9);
memset(NS2BSIndx+11,2*3,256-11);
for (i=0;i < 3;i++)
NS2Indx[i]=i;
for (m=i, k=Step=1;i < 256;i++)
{
NS2Indx[i]=m;
if ( !--k )
{
k = ++Step;
m++;
}
}
memset(HB2Flag,0,0x40);
memset(HB2Flag+0x40,0x08,0x100-0x40);
DummySEE2Cont.Shift=PERIOD_BITS;
}
}
void RARPPM_CONTEXT::rescale(ModelPPM *Model)
{
int OldNS=NumStats, i=NumStats-1, Adder, EscFreq;
RARPPM_STATE* p1, * p;
for (p=Model->FoundState;p != U.Stats;p--)
_PPMD_SWAP(p[0],p[-1]);
U.Stats->Freq += 4;
U.SummFreq += 4;
EscFreq=U.SummFreq-p->Freq;
Adder=(Model->OrderFall != 0);
U.SummFreq = (p->Freq=(p->Freq+Adder) >> 1);
do
{
EscFreq -= (++p)->Freq;
U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1);
if (p[0].Freq > p[-1].Freq)
{
RARPPM_STATE tmp=*(p1=p);
do
{
p1[0]=p1[-1];
} while (--p1 != U.Stats && tmp.Freq > p1[-1].Freq);
*p1=tmp;
}
} while ( --i );
if (p->Freq == 0)
{
do
{
i++;
} while ((--p)->Freq == 0);
EscFreq += i;
if ((NumStats -= i) == 1)
{
RARPPM_STATE tmp=*U.Stats;
do
{
tmp.Freq-=(tmp.Freq >> 1);
EscFreq>>=1;
} while (EscFreq > 1);
Model->SubAlloc.FreeUnits(U.Stats,(OldNS+1) >> 1);
*(Model->FoundState=&OneState)=tmp; return;
}
}
U.SummFreq += (EscFreq -= (EscFreq >> 1));
int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1;
if (n0 != n1)
U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1);
Model->FoundState=U.Stats;
}
inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1)
{
RARPPM_STATE UpState;
RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor;
RARPPM_STATE * p, * ps[MAX_O], ** pps=ps;
if ( !Skip )
{
*pps++ = FoundState;
if ( !pc->Suffix )
goto NO_LOOP;
}
if ( p1 )
{
p=p1;
pc=pc->Suffix;
goto LOOP_ENTRY;
}
do
{
pc=pc->Suffix;
if (pc->NumStats != 1)
{
if ((p=pc->U.Stats)->Symbol != FoundState->Symbol)
do
{
p++;
} while (p->Symbol != FoundState->Symbol);
}
else
p=&(pc->OneState);
LOOP_ENTRY:
if (p->Successor != UpBranch)
{
pc=p->Successor;
break;
}
// We ensure that PPM order input parameter does not exceed MAX_O (64),
// so we do not really need this check and added it for extra safety.
// See CVE-2017-17969 for details.
if (pps>=ps+ASIZE(ps))
return NULL;
*pps++ = p;
} while ( pc->Suffix );
NO_LOOP:
if (pps == ps)
return pc;
UpState.Symbol=*(byte*) UpBranch;
UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1);
if (pc->NumStats != 1)
{
if ((byte*) pc <= SubAlloc.pText)
return(NULL);
if ((p=pc->U.Stats)->Symbol != UpState.Symbol)
do
{
p++;
} while (p->Symbol != UpState.Symbol);
uint cf=p->Freq-1;
uint s0=pc->U.SummFreq-pc->NumStats-cf;
UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0)));
}
else
UpState.Freq=pc->OneState.Freq;
do
{
pc = pc->createChild(this,*--pps,UpState);
if ( !pc )
return NULL;
} while (pps != ps);
return pc;
}
inline void ModelPPM::UpdateModel()
{
RARPPM_STATE fs = *FoundState, *p = NULL;
RARPPM_CONTEXT *pc, *Successor;
uint ns1, ns, cf, sf, s0;
if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL)
{
if (pc->NumStats != 1)
{
if ((p=pc->U.Stats)->Symbol != fs.Symbol)
{
do
{
p++;
} while (p->Symbol != fs.Symbol);
if (p[0].Freq >= p[-1].Freq)
{
_PPMD_SWAP(p[0],p[-1]);
p--;
}
}
if (p->Freq < MAX_FREQ-9)
{
p->Freq += 2;
pc->U.SummFreq += 2;
}
}
else
{
p=&(pc->OneState);
p->Freq += (p->Freq < 32);
}
}
if ( !OrderFall )
{
MinContext=MaxContext=FoundState->Successor=CreateSuccessors(TRUE,p);
if ( !MinContext )
goto RESTART_MODEL;
return;
}
*SubAlloc.pText++ = fs.Symbol;
Successor = (RARPPM_CONTEXT*) SubAlloc.pText;
if (SubAlloc.pText >= SubAlloc.FakeUnitsStart)
goto RESTART_MODEL;
if ( fs.Successor )
{
if ((byte*) fs.Successor <= SubAlloc.pText &&
(fs.Successor=CreateSuccessors(FALSE,p)) == NULL)
goto RESTART_MODEL;
if ( !--OrderFall )
{
Successor=fs.Successor;
SubAlloc.pText -= (MaxContext != MinContext);
}
}
else
{
FoundState->Successor=Successor;
fs.Successor=MinContext;
}
s0=MinContext->U.SummFreq-(ns=MinContext->NumStats)-(fs.Freq-1);
for (pc=MaxContext;pc != MinContext;pc=pc->Suffix)
{
if ((ns1=pc->NumStats) != 1)
{
if ((ns1 & 1) == 0)
{
pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1);
if ( !pc->U.Stats )
goto RESTART_MODEL;
}
pc->U.SummFreq += (2*ns1 < ns)+2*((4*ns1 <= ns) & (pc->U.SummFreq <= 8*ns1));
}
else
{
p=(RARPPM_STATE*) SubAlloc.AllocUnits(1);
if ( !p )
goto RESTART_MODEL;
*p=pc->OneState;
pc->U.Stats=p;
if (p->Freq < MAX_FREQ/4-1)
p->Freq += p->Freq;
else
p->Freq = MAX_FREQ-4;
pc->U.SummFreq=p->Freq+InitEsc+(ns > 3);
}
cf=2*fs.Freq*(pc->U.SummFreq+6);
sf=s0+pc->U.SummFreq;
if (cf < 6*sf)
{
cf=1+(cf > sf)+(cf >= 4*sf);
pc->U.SummFreq += 3;
}
else
{
cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf);
pc->U.SummFreq += (ushort)cf;
}
p=pc->U.Stats+ns1;
p->Successor=Successor;
p->Symbol = fs.Symbol;
p->Freq = (byte)cf;
pc->NumStats=(ushort)++ns1;
}
MaxContext=MinContext=fs.Successor;
return;
RESTART_MODEL:
RestartModelRare();
EscCount=0;
}
// Tabulated escapes for exponential symbol distribution
static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
#define GET_MEAN(SUMM,SHIFT,ROUND) ((SUMM+(1 << (SHIFT-ROUND))) >> (SHIFT))
inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
{
RARPPM_STATE& rs=OneState;
Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol];
ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+
Model->NS2BSIndx[Suffix->NumStats-1]+
Model->HiBitsFlag+2*Model->HB2Flag[rs.Symbol]+
((Model->RunLength >> 26) & 0x20)];
if (Model->Coder.GetCurrentShiftCount(TOT_BITS) < bs)
{
Model->FoundState=&rs;
rs.Freq += (rs.Freq < 128);
Model->Coder.SubRange.LowCount=0;
Model->Coder.SubRange.HighCount=bs;
bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2));
Model->PrevSuccess=1;
Model->RunLength++;
}
else
{
Model->Coder.SubRange.LowCount=bs;
bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2));
Model->Coder.SubRange.HighCount=BIN_SCALE;
Model->InitEsc=ExpEscape[bs >> 10];
Model->NumMasked=1;
Model->CharMask[rs.Symbol]=Model->EscCount;
Model->PrevSuccess=0;
Model->FoundState=NULL;
}
}
inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p)
{
(Model->FoundState=p)->Freq += 4;
U.SummFreq += 4;
if (p[0].Freq > p[-1].Freq)
{
_PPMD_SWAP(p[0],p[-1]);
Model->FoundState=--p;
if (p->Freq > MAX_FREQ)
rescale(Model);
}
}
inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model)
{
Model->Coder.SubRange.scale=U.SummFreq;
RARPPM_STATE* p=U.Stats;
int i, HiCnt;
int count=Model->Coder.GetCurrentCount();
if (count>=(int)Model->Coder.SubRange.scale)
return(false);
if (count < (HiCnt=p->Freq))
{
Model->PrevSuccess=(2*(Model->Coder.SubRange.HighCount=HiCnt) > Model->Coder.SubRange.scale);
Model->RunLength += Model->PrevSuccess;
(Model->FoundState=p)->Freq=(HiCnt += 4);
U.SummFreq += 4;
if (HiCnt > MAX_FREQ)
rescale(Model);
Model->Coder.SubRange.LowCount=0;
return(true);
}
else
if (Model->FoundState==NULL)
return(false);
Model->PrevSuccess=0;
i=NumStats-1;
while ((HiCnt += (++p)->Freq) <= count)
if (--i == 0)
{
Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol];
Model->Coder.SubRange.LowCount=HiCnt;
Model->CharMask[p->Symbol]=Model->EscCount;
i=(Model->NumMasked=NumStats)-1;
Model->FoundState=NULL;
do
{
Model->CharMask[(--p)->Symbol]=Model->EscCount;
} while ( --i );
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
return(true);
}
Model->Coder.SubRange.LowCount=(Model->Coder.SubRange.HighCount=HiCnt)-p->Freq;
update1(Model,p);
return(true);
}
inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p)
{
(Model->FoundState=p)->Freq += 4;
U.SummFreq += 4;
if (p->Freq > MAX_FREQ)
rescale(Model);
Model->EscCount++;
Model->RunLength=Model->InitRL;
}
inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff)
{
RARPPM_SEE2_CONTEXT* psee2c;
if (NumStats != 256)
{
psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+
(Diff < Suffix->NumStats-NumStats)+
2*(U.SummFreq < 11*NumStats)+4*(Model->NumMasked > Diff)+
Model->HiBitsFlag;
Model->Coder.SubRange.scale=psee2c->getMean();
}
else
{
psee2c=&Model->DummySEE2Cont;
Model->Coder.SubRange.scale=1;
}
return psee2c;
}
inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
{
int count, HiCnt, i=NumStats-Model->NumMasked;
RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i);
RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1;
HiCnt=0;
do
{
do
{
p++;
} while (Model->CharMask[p->Symbol] == Model->EscCount);
HiCnt += p->Freq;
// We do not reuse PPMd coder in unstable state, so we do not really need
// this check and added it for extra safety. See CVE-2017-17969 for details.
if (pps>=ps+ASIZE(ps))
return false;
*pps++ = p;
} while ( --i );
Model->Coder.SubRange.scale += HiCnt;
count=Model->Coder.GetCurrentCount();
if (count>=(int)Model->Coder.SubRange.scale)
return(false);
p=*(pps=ps);
if (count < HiCnt)
{
HiCnt=0;
while ((HiCnt += p->Freq) <= count)
{
pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
p=*pps;
}
Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq;
psee2c->update();
update2(Model,p);
}
else
{
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
// 2022.12.02: we removed pps-- here and changed the code below to avoid
// "array subscript -1 is outside array bounds" warning in some compilers.
do
{
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount;
pps++;
} while ( --i );
psee2c->Summ += (ushort)Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
}
return true;
}
inline void ModelPPM::ClearMask()
{
EscCount=1;
memset(CharMask,0,sizeof(CharMask));
}
// reset PPM variables after data error allowing safe resuming
// of further data processing
void ModelPPM::CleanUp()
{
SubAlloc.StopSubAllocator();
SubAlloc.StartSubAllocator(1);
StartModelRare(2);
}
bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar)
{
int MaxOrder=UnpackRead->GetChar();
bool Reset=(MaxOrder & 0x20)!=0;
int MaxMB;
if (Reset)
MaxMB=UnpackRead->GetChar();
else
if (SubAlloc.GetAllocatedMemory()==0)
return(false);
if (MaxOrder & 0x40)
EscChar=UnpackRead->GetChar();
Coder.InitDecoder(UnpackRead);
if (Reset)
{
MaxOrder=(MaxOrder & 0x1f)+1;
if (MaxOrder>16)
MaxOrder=16+(MaxOrder-16)*3;
if (MaxOrder==1)
{
SubAlloc.StopSubAllocator();
return(false);
}
SubAlloc.StartSubAllocator(MaxMB+1);
StartModelRare(MaxOrder);
}
return(MinContext!=NULL);
}
int ModelPPM::DecodeChar()
{
if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd)
return(-1);
if (MinContext->NumStats != 1)
{
if ((byte*)MinContext->U.Stats <= SubAlloc.pText || (byte*)MinContext->U.Stats>SubAlloc.HeapEnd)
return(-1);
if (!MinContext->decodeSymbol1(this))
return(-1);
}
else
MinContext->decodeBinSymbol(this);
Coder.Decode();
while ( !FoundState )
{
ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead);
do
{
OrderFall++;
MinContext=MinContext->Suffix;
if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd)
return(-1);
} while (MinContext->NumStats == NumMasked);
if (!MinContext->decodeSymbol2(this))
return(-1);
Coder.Decode();
}
int Symbol=FoundState->Symbol;
if (!OrderFall && (byte*) FoundState->Successor > SubAlloc.pText)
MinContext=MaxContext=FoundState->Successor;
else
{
UpdateModel();
if (EscCount == 0)
ClearMask();
}
ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead);
return(Symbol);
}
+122
View File
@@ -0,0 +1,122 @@
#ifndef _RAR_PPMMODEL_
#define _RAR_PPMMODEL_
#include "coder.hpp"
#include "suballoc.hpp"
#ifdef ALLOW_MISALIGNED
#pragma pack(1)
#endif
struct RARPPM_DEF
{
static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS,
INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124;
};
struct RARPPM_SEE2_CONTEXT : RARPPM_DEF
{ // SEE-contexts for PPM-contexts with masked symbols
ushort Summ;
byte Shift, Count;
void init(int InitVal)
{
Summ=InitVal << (Shift=PERIOD_BITS-4);
Count=4;
}
uint getMean()
{
short RetVal=GET_SHORT16(Summ) >> Shift;
Summ -= RetVal;
return RetVal+(RetVal == 0);
}
void update()
{
if (Shift < PERIOD_BITS && --Count == 0)
{
Summ += Summ;
Count=3 << Shift++;
}
}
};
class ModelPPM;
struct RARPPM_CONTEXT;
struct RARPPM_STATE
{
byte Symbol;
byte Freq;
RARPPM_CONTEXT* Successor;
};
struct RARPPM_CONTEXT : RARPPM_DEF
{
ushort NumStats;
struct FreqData
{
ushort SummFreq;
RARPPM_STATE RARPPM_PACK_ATTR * Stats;
};
union
{
FreqData U;
RARPPM_STATE OneState;
};
RARPPM_CONTEXT* Suffix;
inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder:
inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context
inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix
inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor
inline bool decodeSymbol1(ModelPPM *Model); // other orders:
inline bool decodeSymbol2(ModelPPM *Model); // BCD context
inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix
inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor
void rescale(ModelPPM *Model);
inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState);
inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff);
};
#ifdef ALLOW_MISALIGNED
#ifdef _AIX
#pragma pack(pop)
#else
#pragma pack()
#endif
#endif
class ModelPPM : RARPPM_DEF
{
private:
friend struct RARPPM_CONTEXT;
RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont;
struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext;
RARPPM_STATE* FoundState; // found next state transition
int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL;
byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
byte EscCount, PrevSuccess, HiBitsFlag;
ushort BinSumm[128][64]; // binary SEE-contexts
RangeCoder Coder;
SubAllocator SubAlloc;
void RestartModelRare();
void StartModelRare(int MaxOrder);
inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1);
inline void UpdateModel();
inline void ClearMask();
public:
ModelPPM();
void CleanUp(); // reset PPM variables after data error
bool DecodeInit(Unpack *UnpackRead,int &EscChar);
int DecodeChar();
};
#endif
+134
View File
@@ -0,0 +1,134 @@
#include "rar.hpp"
/* Zone.Identifier stream can include the text like:
[ZoneTransfer]
ZoneId=3
HostUrl=https://site/path/file.ext
ReferrerUrl=d:\path\archive.ext
Where ZoneId can be:
0 = My Computer
1 = Local intranet
2 = Trusted sites
3 = Internet
4 = Restricted sites
*/
MarkOfTheWeb::MarkOfTheWeb()
{
ZoneIdValue=-1; // -1 indicates the missing MOTW.
AllFields=false;
}
void MarkOfTheWeb::Clear()
{
ZoneIdValue=-1;
}
void MarkOfTheWeb::ReadZoneIdStream(const std::wstring &FileName,bool AllFields)
{
MarkOfTheWeb::AllFields=AllFields;
ZoneIdValue=-1;
ZoneIdStream.clear();
std::wstring StreamName=FileName+MOTW_STREAM_NAME;
File SrcFile;
if (SrcFile.Open(StreamName))
{
ZoneIdStream.resize(MOTW_STREAM_MAX_SIZE);
int BufSize=SrcFile.Read(&ZoneIdStream[0],ZoneIdStream.size());
ZoneIdStream.resize(BufSize<0 ? 0:BufSize);
if (BufSize<=0)
return;
ZoneIdValue=ParseZoneIdStream(ZoneIdStream);
}
}
// 'Stream' contains the raw "Zone.Identifier" NTFS stream data on input
// and either raw or cleaned stream data on output.
int MarkOfTheWeb::ParseZoneIdStream(std::string &Stream)
{
if (!starts_with(Stream,"[ZoneTransfer]"))
return -1; // Not a valid Mark of the Web. Prefer the archive MOTW if any.
std::string::size_type ZoneId=Stream.find("ZoneId=",0);
if (ZoneId==std::string::npos || !IsDigit(Stream[ZoneId+7]))
return -1; // Not a valid Mark of the Web.
int ZoneIdValue=atoi(&Stream[ZoneId+7]);
if (ZoneIdValue<0 || ZoneIdValue>4)
return -1; // Not a valid Mark of the Web.
if (!AllFields)
Stream="[ZoneTransfer]\r\nZoneId=" + std::to_string(ZoneIdValue) + "\r\n";
return ZoneIdValue;
}
void MarkOfTheWeb::CreateZoneIdStream(const std::wstring &Name,StringList &MotwList)
{
if (ZoneIdValue==-1)
return;
size_t ExtPos=GetExtPos(Name);
const wchar *Ext=ExtPos==std::wstring::npos ? L"":&Name[ExtPos+1];
bool Matched=false;
const wchar *CurMask;
MotwList.Rewind();
while ((CurMask=MotwList.GetString())!=nullptr)
{
// Perform the fast extension comparison for simple *.ext masks.
// Also we added the fast path to wcsicomp for English only strings.
// When extracting 100000 files with "Exe and office" masks set
// this loop spent 85ms with this optimization and wcsicomp optimized
// for English strings, 415ms with this optimization only, 475ms with
// wcsicomp optimized only and 795ms without both optimizations.
bool FastCmp=CurMask[0]=='*' && CurMask[1]=='.' && wcspbrk(CurMask+2,L"*?")==NULL;
if (FastCmp && wcsicomp(Ext,CurMask+2)==0 || !FastCmp && CmpName(CurMask,Name,MATCH_NAMES))
{
Matched=true;
break;
}
}
if (!Matched)
return;
std::wstring StreamName=Name+MOTW_STREAM_NAME;
File StreamFile;
if (StreamFile.Create(StreamName)) // Can fail on FAT.
{
// We got a report that write to stream failed on Synology 2411+ NAS drive.
// So we handle it silently instead of aborting.
StreamFile.SetExceptions(false);
if (StreamFile.Write(&ZoneIdStream[0],ZoneIdStream.size()))
StreamFile.Close();
}
}
bool MarkOfTheWeb::IsNameConflicting(const std::wstring &StreamName)
{
// We must use the case insensitive comparison for L":Zone.Identifier"
// to catch specially crafted archived streams like L":zone.identifier".
return wcsicomp(StreamName,MOTW_STREAM_NAME)==0 && ZoneIdValue!=-1;
}
// Return true and prepare the file stream to write if its ZoneId is stricter
// than archive ZoneId. If it is missing, less or equally strict, return false.
bool MarkOfTheWeb::IsFileStreamMoreSecure(std::string &FileStream)
{
int StreamZone=ParseZoneIdStream(FileStream);
return StreamZone>ZoneIdValue;
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef _RAR_MOTW_
#define _RAR_MOTW_
class MarkOfTheWeb
{
private:
const size_t MOTW_STREAM_MAX_SIZE=1024;
const wchar* MOTW_STREAM_NAME=L":Zone.Identifier"; // Must start from ':'.
int ParseZoneIdStream(std::string &Stream);
std::string ZoneIdStream; // Store archive ":Zone.Identifier" NTFS stream data.
int ZoneIdValue; // -1 if missing.
bool AllFields; // Copy all MOTW fields or ZoneId only.
public:
MarkOfTheWeb();
void Clear();
void ReadZoneIdStream(const std::wstring &FileName,bool AllFields);
void CreateZoneIdStream(const std::wstring &Name,StringList &MotwList);
bool IsNameConflicting(const std::wstring &StreamName);
bool IsFileStreamMoreSecure(std::string &FileStream);
};
#endif
+28
View File
@@ -0,0 +1,28 @@
#include "rar.hpp"
RAROptions::RAROptions()
{
Init();
}
void RAROptions::Init()
{
memset(this,0,sizeof(RAROptions));
WinSize=0x2000000;
WinSizeLimit=0x100000000;
Overwrite=OVERWRITE_DEFAULT;
Method=3;
MsgStream=MSG_STDOUT;
ConvertNames=NAMES_ORIGINALCASE;
xmtime=EXTTIME_MAX;
FileSizeLess=INT64NDF;
FileSizeMore=INT64NDF;
HashType=HASH_CRC32;
#ifdef RAR_SMP
Threads=GetNumberOfThreads();
#endif
#ifdef USE_QOPEN
QOpenMode=QOPEN_AUTO;
#endif
}
+243
View File
@@ -0,0 +1,243 @@
#ifndef _RAR_OPTIONS_
#define _RAR_OPTIONS_
#define DEFAULT_RECOVERY 3
#define DEFAULT_RECVOLUMES -10
#define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size.
enum PATH_EXCL_MODE {
EXCL_UNCHANGED=0, // Process paths as is (default).
EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely)
EXCL_BASEPATH, // -ep1 (exclude the base part of path)
EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter)
EXCL_ABSPATH // -ep3 (the full path with the disk letter)
};
enum {
SOLID_NONE=0, // Non-solid mode.
SOLID_NORMAL=1, // Standard solid mode.
SOLID_COUNT=2, // Reset the solid data after specified file count.
SOLID_FILEEXT=4, // Reset the solid data for every new file extension.
SOLID_VOLUME_DEPENDENT=8, // Preserve solid data in all volumes.
SOLID_VOLUME_INDEPENDENT=16, // Reset solid data in all volumes.
SOLID_RESET=32, // Reset solid data for newly added files.
SOLID_BLOCK_SIZE=64 // Reset solid data after the specified size.
};
enum ARCTIME_MODE {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST,ARCTIME_SPECIFIED};
enum EXTTIME_MODE {
EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX
};
enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE};
enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL};
enum RECURSE_MODE
{
RECURSE_NONE=0, // no recurse switches
RECURSE_DISABLE, // switch -r-
RECURSE_ALWAYS, // switch -r
RECURSE_WILDCARDS // switch -r0
};
enum OVERWRITE_MODE
{
OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving.
OVERWRITE_ALL,
OVERWRITE_NONE,
OVERWRITE_AUTORENAME,
OVERWRITE_FORCE_ASK
};
enum ARC_METADATA
{
ARCMETA_NONE=0,
ARCMETA_SAVE, // -ams
ARCMETA_RESTORE // -amr
};
enum QOPEN_MODE { QOPEN_NONE=0, QOPEN_AUTO, QOPEN_ALWAYS };
enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 };
#define MAX_FILTER_TYPES 16
enum FilterState {
FILTER_DEFAULT=0, // No -mc<filter> switch.
FILTER_AUTO, // -mc<filter> switch is present.
FILTER_FORCE, // -mc<filter>+ switch is present.
FILTER_DISABLE // -mc<filter>- switch is present.
};
enum SAVECOPY_MODE {
SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT,
SAVECOPY_DUPLISTEXIT
};
enum APPENDARCNAME_MODE
{
APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR,
APPENDARCNAME_OWNDIR
};
enum POWER_MODE {
POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP,
POWERMODE_RESTART
};
// Need "forced off" state to turn off sound in GUI command line.
enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF};
struct FilterMode
{
FilterState State;
int Param1;
int Param2;
};
#define MAX_GENERATE_MASK 128
// Here we store simple data types, which we can clear and move all together
// quickly. Rest of data types goes to CommandData.
class RAROptions
{
public:
RAROptions();
void Init();
uint ExclFileAttr;
uint InclFileAttr;
// We handle -ed and -e+d with special flags instead of attribute mask,
// so it works with both Windows and Unix archives.
bool ExclDir;
bool InclDir;
bool InclAttrSet;
uint64 WinSize;
uint64 WinSizeLimit; // Switch -mdx<size>.
#ifdef USE_QOPEN
QOPEN_MODE QOpenMode;
#endif
bool ConfigDisabled; // Switch -cfg-.
RAR_CHARSET CommentCharset;
RAR_CHARSET FilelistCharset;
RAR_CHARSET ErrlogCharset;
RAR_CHARSET RedirectCharset;
bool EncryptHeaders;
bool SkipEncrypted;
bool ManualPassword; // Password entered manually during operation, might need to clean for next archive.
MESSAGE_TYPE MsgStream;
SOUND_NOTIFY_MODE Sound;
OVERWRITE_MODE Overwrite;
int Method;
HASH_TYPE HashType;
uint Recovery;
int RecVolNumber;
ARC_METADATA ArcMetadata;
bool DisablePercentage;
bool DisableCopyright;
bool DisableDone;
bool DisableNames;
bool PrintVersion;
uint Solid;
uint SolidCount;
uint64 SolidBlockSize;
bool ClearArc;
bool AddArcOnly;
bool DisableComment;
bool FreshFiles;
bool UpdateFiles;
PATH_EXCL_MODE ExclPath;
RECURSE_MODE Recurse;
int64 VolSize;
uint CurVolNum;
bool AllYes;
bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now.
bool DisableSortSolid;
int ConvertNames;
bool ProcessOwners;
bool SaveSymLinks;
bool SaveHardLinks;
bool AbsoluteLinks;
bool SkipSymLinks;
int Priority;
int SleepTime;
bool UseLargePages;
// Quit after processing some system integration related switch,
// like enabling the large memory pages privilege.
// menu for non-admin user and quit.
bool SetupComplete;
bool KeepBroken;
bool OpenShared;
bool DeleteFiles;
#ifdef _WIN_ALL
bool AllowIncompatNames; // Allow names with trailing dots and spaces.
#endif
#ifndef SFX_MODULE
bool GenerateArcName;
wchar GenerateMask[MAX_GENERATE_MASK];
wchar DefGenerateMask[MAX_GENERATE_MASK];
#endif
bool SyncFiles;
bool ProcessEA;
bool SaveStreams;
#ifdef PROPAGATE_MOTW
bool MotwAllFields;
#endif
bool SetCompressedAttr;
bool IgnoreGeneralAttr;
RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore;
bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR;
RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter;
bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR;
int64 FileSizeLess;
int64 FileSizeMore;
bool Lock;
bool Test;
bool VolumePause;
FilterMode FilterModes[MAX_FILTER_TYPES];
uint VersionControl;
APPENDARCNAME_MODE AppendArcNameToPath;
POWER_MODE Shutdown;
EXTTIME_MODE xmtime; // Extended time modes (time precision to store).
EXTTIME_MODE xctime;
EXTTIME_MODE xatime;
bool PreserveAtime;
uint Threads; // We use it to init hash even if RAR_SMP is not defined.
#ifdef RARDLL
int DllOpMode;
int DllError;
LPARAM UserData;
UNRARCALLBACK Callback;
CHANGEVOLPROC ChangeVolProc;
PROCESSDATAPROC ProcessDataProc;
#endif
};
#endif
+274
View File
@@ -0,0 +1,274 @@
#ifndef _RAR_OS_
#define _RAR_OS_
#define FALSE 0
#define TRUE 1
#if defined(RARDLL) && !defined(SILENT)
#define SILENT
#endif
#include <new>
#include <string>
#include <vector>
#include <deque>
#include <memory> // For automatic pointers.
#include <algorithm>
#ifdef _WIN_ALL
#define LITTLE_ENDIAN
// We got a report that just "#define STRICT" is incompatible with
// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order
// in which these statements are reached this may cause a compiler warning
// and build break for other projects incorporating this source.
// So we changed it to "#define STRICT 1".
#ifndef STRICT
#define STRICT 1
#endif
// 'ifndef' check here is needed for unrar.dll header to avoid macro
// re-definition warnings in third party projects.
#ifndef UNICODE
#define UNICODE
#define _UNICODE // Set _T() macro to convert from narrow to wide strings.
#endif
#define WINVER _WIN32_WINNT_WINXP
#define _WIN32_WINNT _WIN32_WINNT_WINXP
#if !defined(ZIPSFX)
#define RAR_SMP
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <prsht.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <PowrProf.h>
#pragma comment(lib, "PowrProf.lib")
#include <psapi.h>
#pragma comment(lib, "Psapi.lib") // For GetProcessMemoryInfo().
#include <shellapi.h>
#include <shlobj.h>
#include <winioctl.h>
#include <wincrypt.h>
#include <wchar.h>
#include <wctype.h>
#include <Sddl.h>
#include <ntsecapi.h>
// For WMI requests.
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
#include <sys/types.h>
#include <sys/stat.h>
#include <dos.h>
#include <direct.h>
#include <intrin.h>
// Use SSE only for x86/x64, not ARM Windows.
#if defined(_M_IX86) || defined(_M_X64)
#define USE_SSE
#define SSE_ALIGNMENT 16
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <dos.h>
#include <io.h>
#include <time.h>
#include <signal.h>
#define SAVE_LINKS
#define ENABLE_ACCESS
#define DefConfigName L"rar.ini"
#define DefLogName L"rar.log"
#define SPATHDIVIDER L"\\"
#define CPATHDIVIDER L'\\'
#define MASKALL L"*"
#define READBINARY "rb"
#define READTEXT "rt"
#define UPDATEBINARY "r+b"
#define CREATEBINARY "w+b"
#define WRITEBINARY "wb"
#define APPENDTEXT "at"
#define _stdfunction __cdecl
#define _forceinline __forceinline
#endif // _WIN_ALL
#ifdef _UNIX
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#if defined(__QNXNTO__)
#include <sys/param.h>
#endif
#ifdef _APPLE
#include <sys/sysctl.h>
#endif
#ifndef SFX_MODULE
#include <sys/statvfs.h>
#endif
#include <pwd.h>
#include <grp.h>
#include <wchar.h>
#include <wctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h> // Needed for ptrdiff_t in some UnRAR source builds.
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <time.h>
#include <signal.h>
#include <utime.h>
#include <locale.h>
#ifdef __GNUC__
#if defined(__i386__) || defined(__x86_64__)
#include <x86intrin.h>
#define USE_SSE
#define SSE_ALIGNMENT 16
#endif
#endif
#if defined(__aarch64__) && (defined(__ARM_FEATURE_CRYPTO) || defined(__ARM_FEATURE_CRC32))
#include <arm_neon.h>
#ifndef _APPLE
#include <sys/auxv.h>
#include <asm/hwcap.h>
#endif
#ifdef __ARM_FEATURE_CRYPTO
#define USE_NEON_AES
#endif
#ifdef __ARM_FEATURE_CRC32
#define USE_NEON_CRC32
#endif
#endif
#ifdef S_IFLNK
#define SAVE_LINKS
#endif
#if defined(__linux) || defined(__FreeBSD__)
#include <sys/time.h>
#define USE_LUTIMES
#endif
#define ENABLE_ACCESS
#define DefConfigName L".rarrc"
#define DefLogName L".rarlog"
#define SPATHDIVIDER L"/"
#define CPATHDIVIDER L'/'
#define MASKALL L"*"
#define READBINARY "r"
#define READTEXT "r"
#define UPDATEBINARY "r+"
#define CREATEBINARY "w+"
#define WRITEBINARY "w"
#define APPENDTEXT "a"
#define _stdfunction
#define _forceinline inline
#ifdef _APPLE
#if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN)
#define BIG_ENDIAN
#undef LITTLE_ENDIAN
#endif
#if defined(__i386__) && !defined(LITTLE_ENDIAN)
#define LITTLE_ENDIAN
#undef BIG_ENDIAN
#endif
#endif
#if defined(__sparc) || defined(sparc) || defined(__hpux)
#ifndef BIG_ENDIAN
#define BIG_ENDIAN
#endif
#endif
#ifdef __VMS
# define LITTLE_ENDIAN
#endif
// Unlike Apple x64, utimensat shall be available in all Apple M1 systems.
#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__)
#define UNIX_TIME_NS // Nanosecond time precision in Unix.
#endif
#endif // _UNIX
typedef const wchar* MSGID;
#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required.
#define SSE_ALIGNMENT 1
#endif
// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN.
#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN)
#define LITTLE_ENDIAN
#endif
#if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN)
#define BIG_ENDIAN
#endif
#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
#if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64)
#define LITTLE_ENDIAN
#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__)
#define LITTLE_ENDIAN
#elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__)
#define BIG_ENDIAN
#else
#error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them."
#endif
#endif
#if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
#undef LITTLE_ENDIAN
#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
#undef BIG_ENDIAN
#else
#error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef one of them."
#endif
#endif
#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
// Allow unaligned integer access, increases speed in some operations.
#define ALLOW_MISALIGNED
#endif
#endif // _RAR_OS_
+1242
View File
File diff suppressed because it is too large. Load diff
+120
View File
@@ -0,0 +1,120 @@
#ifndef _RAR_PATHFN_
#define _RAR_PATHFN_
wchar* PointToName(const wchar *Path);
std::wstring PointToName(const std::wstring &Path);
size_t GetNamePos(const std::wstring &Path);
wchar* PointToLastChar(const wchar *Path);
wchar GetLastChar(const std::wstring &Path);
size_t ConvertPath(const std::wstring *SrcPath,std::wstring *DestPath);
void SetName(std::wstring &FullName,const std::wstring &Name);
void SetExt(std::wstring &Name,std::wstring NewExt);
void RemoveExt(std::wstring &Name);
void SetSFXExt(std::wstring &SFXName);
wchar *GetExt(const wchar *Name);
std::wstring GetExt(const std::wstring &Name);
std::wstring::size_type GetExtPos(const std::wstring &Name);
bool CmpExt(const std::wstring &Name,const std::wstring &Ext);
bool IsWildcard(const std::wstring &Str);
bool IsPathDiv(int Ch);
bool IsDriveDiv(int Ch);
bool IsDriveLetter(const std::wstring &Path);
int GetPathDisk(const std::wstring &Path);
void AddEndSlash(std::wstring &Path);
void MakeName(const std::wstring &Path,const std::wstring &Name,std::wstring &Pathname);
void GetPathWithSep(const std::wstring &FullName,std::wstring &Path);
void RemoveNameFromPath(std::wstring &Path);
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
bool GetAppDataPath(std::wstring &Path,bool Create);
void GetRarDataPath(std::wstring &Path,bool Create);
#endif
#ifdef _WIN_ALL
bool SHGetPathStrFromIDList(PCIDLIST_ABSOLUTE pidl,std::wstring &Path);
#endif
#ifndef SFX_MODULE
bool EnumConfigPaths(uint Number,std::wstring &Path,bool Create);
void GetConfigName(const std::wstring &Name,std::wstring &FullName,bool CheckExist,bool Create);
#endif
size_t GetVolNumPos(const std::wstring &ArcName);
void NextVolumeName(std::wstring &ArcName,bool OldNumbering);
bool IsNameUsable(const std::wstring &Name);
void MakeNameUsable(std::wstring &Name,bool Extended);
void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);
void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength);
void UnixSlashToDos(const std::string &SrcName,std::string &DestName);
void UnixSlashToDos(const std::wstring &SrcName,std::wstring &DestName);
void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength);
void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength);
void DosSlashToUnix(const std::string &SrcName,std::string &DestName);
void DosSlashToUnix(const std::wstring &SrcName,std::wstring &DestName);
inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName,MaxLength);
#else
DosSlashToUnix(SrcName,DestName,MaxLength);
#endif
}
inline void SlashToNative(const std::string &SrcName,std::string &DestName)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName);
#else
DosSlashToUnix(SrcName,DestName);
#endif
}
inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName,MaxLength);
#else
DosSlashToUnix(SrcName,DestName,MaxLength);
#endif
}
inline void SlashToNative(const std::wstring &SrcName,std::wstring &DestName)
{
#ifdef _WIN_ALL
UnixSlashToDos(SrcName,DestName);
#else
DosSlashToUnix(SrcName,DestName);
#endif
}
void ConvertNameToFull(const std::wstring &Src,std::wstring &Dest);
bool IsFullPath(const std::wstring &Path);
bool IsFullRootPath(const std::wstring &Path);
void GetPathRoot(const std::wstring &Path,std::wstring &Root);
int ParseVersionFileName(std::wstring &Name,bool Truncate);
size_t VolNameToFirstName(const std::wstring &VolName,std::wstring &FirstName,bool NewNumbering);
#ifndef SFX_MODULE
void GenerateArchiveName(std::wstring &ArcName,const std::wstring &GenerateMask,bool Archiving);
#endif
#ifdef _WIN_ALL
bool GetWinLongPath(const std::wstring &Src,std::wstring &Dest);
void ConvertToPrecomposed(std::wstring &Name);
void MakeNameCompatible(std::wstring &Name);
#endif
#ifdef _WIN_ALL
std::wstring GetModuleFileStr();
std::wstring GetProgramFile(const std::wstring &Name);
#endif
#if defined(_WIN_ALL)
bool SetCurDir(const std::wstring &Dir);
#endif
#ifdef _WIN_ALL
bool GetCurDir(std::wstring &Dir);
#endif
#endif
+300
View File
@@ -0,0 +1,300 @@
#include "rar.hpp"
QuickOpen::QuickOpen()
{
Buf=NULL;
Init(NULL,false);
}
QuickOpen::~QuickOpen()
{
Close();
delete[] Buf;
}
void QuickOpen::Init(Archive *Arc,bool WriteMode)
{
if (Arc!=NULL) // Unless called from constructor.
Close();
QuickOpen::Arc=Arc;
QuickOpen::WriteMode=WriteMode;
ListStart=NULL;
ListEnd=NULL;
if (Buf==NULL)
Buf=new byte[MaxBufSize];
CurBufSize=0; // Current size of buffered data in write mode.
Loaded=false;
}
void QuickOpen::Close()
{
QuickOpenItem *Item=ListStart;
while (Item!=NULL)
{
QuickOpenItem *Next=Item->Next;
delete[] Item->Header;
delete Item;
Item=Next;
}
}
void QuickOpen::Load(uint64 BlockPos)
{
if (!Loaded)
{
// If loading for the first time, perform additional intialization.
SeekPos=Arc->Tell();
UnsyncSeekPos=false;
int64 SavePos=SeekPos;
Arc->Seek(BlockPos,SEEK_SET);
// If BlockPos points to original main header, we'll have the infinite
// recursion, because ReadHeader() for main header will attempt to load
// QOpen and call QuickOpen::Load again. If BlockPos points to long chain
// of other main headers, we'll have multiple recursive calls of this
// function wasting resources. So we prohibit QOpen temporarily to
// prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
// and QOpenOffset fields, so we cannot use them to prohibit QOpen.
Arc->SetProhibitQOpen(true);
size_t ReadSize=Arc->ReadHeader();
Arc->SetProhibitQOpen(false);
if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
!Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
{
Arc->Seek(SavePos,SEEK_SET);
return;
}
QOHeaderPos=Arc->CurBlockPos;
RawDataStart=Arc->Tell();
RawDataSize=Arc->SubHead.UnpSize;
Arc->Seek(SavePos,SEEK_SET);
Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
}
if (Arc->SubHead.Encrypted)
{
CommandData *Cmd=Arc->GetCommandData();
#ifndef RAR_NOCRYPT
if (Cmd->Password.IsSet())
Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
else
#endif
{
Loaded=false;
return;
}
}
RawDataPos=0;
ReadBufSize=0;
ReadBufPos=0;
LastReadHeader.clear();
LastReadHeaderPos=0;
ReadBuffer();
}
bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
{
if (!Loaded)
return false;
// Find next suitable cached block.
while (LastReadHeaderPos+LastReadHeader.size()<=SeekPos)
if (!ReadNext())
break;
if (!Loaded)
{
// If something wrong happened, let's set the correct file pointer
// and stop further quick open processing.
if (UnsyncSeekPos)
Arc->File::Seek(SeekPos,SEEK_SET);
return false;
}
if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.size())
{
memcpy(Data,&LastReadHeader[size_t(SeekPos-LastReadHeaderPos)],Size);
Result=Size;
SeekPos+=Size;
UnsyncSeekPos=true;
}
else
{
if (UnsyncSeekPos)
{
Arc->File::Seek(SeekPos,SEEK_SET);
UnsyncSeekPos=false;
}
int ReadSize=Arc->File::Read(Data,Size);
if (ReadSize<0)
{
Loaded=false;
return false;
}
Result=ReadSize;
SeekPos+=ReadSize;
}
return true;
}
bool QuickOpen::Seek(int64 Offset,int Method)
{
if (!Loaded)
return false;
// Normally we process an archive sequentially from beginning to end,
// so we read quick open data sequentially. But some operations like
// archive updating involve several passes. So if we detect that file
// pointer is moved back, we reload quick open data from beginning.
if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
Load(QOHeaderPos);
if (Method==SEEK_SET)
SeekPos=Offset;
if (Method==SEEK_CUR)
SeekPos+=Offset;
UnsyncSeekPos=true;
if (Method==SEEK_END)
{
Arc->File::Seek(Offset,SEEK_END);
SeekPos=Arc->File::Tell();
UnsyncSeekPos=false;
}
return true;
}
bool QuickOpen::Tell(int64 *Pos)
{
if (!Loaded)
return false;
*Pos=SeekPos;
return true;
}
uint QuickOpen::ReadBuffer()
{
int64 SavePos=Arc->Tell();
Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
if (Arc->SubHead.Encrypted)
SizeToRead &= ~CRYPT_BLOCK_MASK;
int ReadSize=0;
if (SizeToRead!=0)
{
ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
if (ReadSize<=0)
ReadSize=0;
else
{
#ifndef RAR_NOCRYPT
if (Arc->SubHead.Encrypted)
Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
#endif
RawDataPos+=ReadSize;
ReadBufSize+=ReadSize;
}
}
Arc->Seek(SavePos,SEEK_SET);
return ReadSize;
}
// Fill RawRead object from buffer.
bool QuickOpen::ReadRaw(RawRead &Raw)
{
if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
{
// Ensure that we have enough data to read CRC and header size.
size_t DataLeft=ReadBufSize-ReadBufPos;
memcpy(Buf,Buf+ReadBufPos,DataLeft);
ReadBufPos=0;
ReadBufSize=DataLeft;
ReadBuffer();
}
const size_t FirstReadSize=7;
if (ReadBufPos+FirstReadSize>ReadBufSize)
return false;
Raw.Read(Buf+ReadBufPos,FirstReadSize);
ReadBufPos+=FirstReadSize;
uint SavedCRC=Raw.Get4();
uint SizeBytes=Raw.GetVSize(4);
uint64 BlockSize=Raw.GetV();
int SizeToRead=int(BlockSize);
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
{
Loaded=false; // Invalid data.
return false;
}
// If rest of block data crosses Buf boundary, read it in loop.
while (SizeToRead>0)
{
size_t DataLeft=ReadBufSize-ReadBufPos;
size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
Raw.Read(Buf+ReadBufPos,CurSizeToRead);
ReadBufPos+=CurSizeToRead;
SizeToRead-=int(CurSizeToRead);
if (SizeToRead>0) // We read the entire buffer and still need more data.
{
ReadBufPos=0;
ReadBufSize=0;
if (ReadBuffer()==0)
return false;
}
}
return SavedCRC==Raw.GetCRC50();
}
// Read next cached header.
bool QuickOpen::ReadNext()
{
RawRead Raw(NULL);
if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
return false;
uint Flags=(uint)Raw.GetV();
uint64 Offset=Raw.GetV();
size_t HeaderSize=(size_t)Raw.GetV();
if (HeaderSize>MAX_HEADER_SIZE_RAR5)
return false;
LastReadHeader.resize(HeaderSize);
Raw.GetB(LastReadHeader.data(),HeaderSize);
// Calculate the absolute position as offset from quick open service header.
LastReadHeaderPos=QOHeaderPos-Offset;
return true;
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef _RAR_QOPEN_
#define _RAR_QOPEN_
struct QuickOpenItem
{
byte *Header;
size_t HeaderSize;
uint64 ArcPos;
QuickOpenItem *Next;
};
class Archive;
class RawRead;
class QuickOpen
{
private:
void Close();
uint ReadBuffer();
bool ReadRaw(RawRead &Raw);
bool ReadNext();
Archive *Arc;
bool WriteMode;
QuickOpenItem *ListStart;
QuickOpenItem *ListEnd;
byte *Buf; // Read quick open data here.
static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE.
size_t CurBufSize; // Current size of buffered data in write mode.
#ifndef RAR_NOCRYPT // For shell extension.
CryptData Crypt;
#endif
bool Loaded;
uint64 QOHeaderPos; // Main QO header position.
uint64 RawDataStart; // Start of QO data, just after the main header.
uint64 RawDataSize; // Size of entire QO data.
uint64 RawDataPos; // Current read position in QO data.
size_t ReadBufSize; // Size of Buf data currently read from QO.
size_t ReadBufPos; // Current read position in Buf data.
std::vector<byte> LastReadHeader;
uint64 LastReadHeaderPos;
uint64 SeekPos;
bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer.
public:
QuickOpen();
~QuickOpen();
void Init(Archive *Arc,bool WriteMode);
void Load(uint64 BlockPos);
void Unload() { Loaded=false; }
bool Read(void *Data,size_t Size,size_t &Result);
bool Seek(int64 Offset,int Method);
bool Tell(int64 *Pos);
};
#endif
+111
View File
@@ -0,0 +1,111 @@
#include "rar.hpp"
#if !defined(RARDLL)
int main(int argc, char *argv[])
{
#ifdef _UNIX
setlocale(LC_ALL,"");
#endif
InitConsole();
ErrHandler.SetSignalHandlers(true);
#ifdef SFX_MODULE
std::wstring ModuleName;
#ifdef _WIN_ALL
ModuleName=GetModuleFileStr();
#else
CharToWide(argv[0],ModuleName);
#endif
#endif
#ifdef _WIN_ALL
SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
// Must be initialized, normal initialization can be skipped in case of
// exception.
POWER_MODE ShutdownOnClose=POWERMODE_KEEP;
#endif
try
{
// Use std::unique_ptr to free Cmd in case of exception.
std::unique_ptr<CommandData> Cmd(new CommandData);
#ifdef SFX_MODULE
Cmd->Command=L"X";
char *Switch=argc>1 ? argv[1]:NULL;
if (Switch!=NULL && Cmd->IsSwitch(Switch[0]))
{
int UpperCmd=etoupper(Switch[1]);
switch(UpperCmd)
{
case 'T':
case 'V':
// Also copy 't' and 'a' modifiers for -v[t,a], if present.
Cmd->Command.clear();
for (char *c=Switch+1;*c!=0;c++)
Cmd->Command+=etoupper(*c);
break;
case '?':
Cmd->OutHelp(RARX_SUCCESS);
break;
}
}
Cmd->AddArcName(ModuleName);
Cmd->ParseDone();
Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source.
#else // !SFX_MODULE
Cmd->ParseCommandLine(true,argc,argv);
if (!Cmd->ConfigDisabled)
{
Cmd->ReadConfig();
Cmd->ParseEnvVar();
}
Cmd->ParseCommandLine(false,argc,argv);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
ShutdownOnClose=Cmd->Shutdown;
if (ShutdownOnClose!=POWERMODE_KEEP)
ShutdownCheckAnother(true);
#endif
uiInit(Cmd->Sound);
InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset);
ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL);
Cmd->OutTitle();
Cmd->ProcessCommand();
}
catch (RAR_EXIT ErrCode)
{
ErrHandler.SetErrorCode(ErrCode);
}
catch (std::bad_alloc&)
{
ErrHandler.MemoryErrorMsg();
ErrHandler.SetErrorCode(RARX_MEMORY);
}
catch (...)
{
ErrHandler.SetErrorCode(RARX_FATAL);
}
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() &&
!ShutdownCheckAnother(false))
Shutdown(ShutdownOnClose);
#endif
ErrHandler.MainExit=true;
CloseLogOptions();
return ErrHandler.GetErrorCode();
}
#endif
+97
View File
@@ -0,0 +1,97 @@
#ifndef _RAR_RARCOMMON_
#define _RAR_RARCOMMON_
#include "raros.hpp"
#include "rartypes.hpp"
#include "os.hpp"
#ifdef RARDLL
#include "dll.hpp"
#endif
#include "version.hpp"
#include "rardefs.hpp"
#include "rarlang.hpp"
#include "rawint.hpp"
#include "unicode.hpp"
#include "errhnd.hpp"
#include "secpassword.hpp"
#include "strlist.hpp"
#include "timefn.hpp"
#include "sha1.hpp"
#include "sha256.hpp"
#include "blake2s.hpp"
#include "hash.hpp"
#include "options.hpp"
#include "rijndael.hpp"
#include "crypt.hpp"
#include "headers5.hpp"
#include "headers.hpp"
#include "pathfn.hpp"
#include "strfn.hpp"
#ifdef _WIN_ALL
#include "isnt.hpp"
#endif
#ifdef PROPAGATE_MOTW
#include "motw.hpp"
#endif
#include "file.hpp"
#include "crc.hpp"
#include "filefn.hpp"
#include "filestr.hpp"
#include "find.hpp"
#include "scantree.hpp"
#include "getbits.hpp"
#include "rdwrfn.hpp"
#ifdef USE_QOPEN
#include "qopen.hpp"
#endif
#include "archive.hpp"
#include "match.hpp"
#include "cmddata.hpp"
#include "ui.hpp"
#include "filcreat.hpp"
#include "consio.hpp"
#include "system.hpp"
#include "log.hpp"
#include "rawread.hpp"
#include "encname.hpp"
#include "resource.hpp"
#include "compress.hpp"
#include "rarvm.hpp"
#include "model.hpp"
#include "threadpool.hpp"
#include "largepage.hpp"
#include "unpack.hpp"
#include "extinfo.hpp"
#include "extract.hpp"
#include "list.hpp"
#include "rs.hpp"
#include "rs16.hpp"
#include "recvol.hpp"
#include "volume.hpp"
#include "smallfn.hpp"
#include "global.hpp"
#endif
+54
View File
@@ -0,0 +1,54 @@
#ifndef _RAR_DEFS_
#define _RAR_DEFS_
#define Min(x,y) (((x)<(y)) ? (x):(y))
#define Max(x,y) (((x)>(y)) ? (x):(y))
// Universal replacement of abs function.
#define Abs(x) (((x)<0) ? -(x):(x))
#define ASIZE(x) (sizeof(x)/sizeof(x[0]))
// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of
// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword.
// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords
// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR
// to 128 for compatibility and because it is enough for AES-256.
#define MAXPASSWORD 512
#define MAXPASSWORD_RAR 128
// Set some arbitrary sensible limit to maximum path length to prevent
// the excessive memory allocation for dynamically allocated strings.
#define MAXPATHSIZE 0x10000
#define MAXSFXSIZE 0x400000
#define MAXCMTSIZE 0x40000
#ifdef _WIN_32
#define DefSFXName L"default32.sfx"
#else
#define DefSFXName L"default.sfx"
#endif
#define DefSortListName L"rarfiles.lst"
// Maximum dictionary allowed by compression. Can be less than
// maximum dictionary supported by decompression.
#define PACK_MAX_DICT 0x1000000000ULL // 64 GB.
// Maximum dictionary allowed by decompression.
#define UNPACK_MAX_DICT 0x1000000000ULL // 64 GB.
#ifndef SFX_MODULE
#define USE_QOPEN
#endif
// Produce the value, which is equal or larger than 'v' and aligned to 'a'.
#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) )
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
#define PROPAGATE_MOTW // Propagate the archive Mark of the Web.
#endif
#endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef _RAR_LANG_
#define _RAR_LANG_
#ifdef USE_RC
#include "rarres.hpp"
#else
#include "loclang.hpp"
#endif
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef _RAR_RAROS_
#define _RAR_RAROS_
#if defined(__WIN32__) || defined(_WIN32)
#define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop.
#ifdef _M_X64
#define _WIN_64
#else
#define _WIN_32
#endif
#endif
#if defined(ANDROID) || defined(__ANDROID__)
#define _UNIX
#define _ANDROID
#endif
#ifdef __APPLE__
#define _UNIX
#define _APPLE
#endif
#if !defined(_WIN_ALL) && !defined(_UNIX)
#define _UNIX
#endif
#endif
+2
View File
@@ -0,0 +1,2 @@
// We use rarpch.cpp to create precompiled headers for MS Visual C++.
#include "rar.hpp"
+32
View File
@@ -0,0 +1,32 @@
#ifndef _RAR_TYPES_
#define _RAR_TYPES_
#include <stdint.h>
typedef uint8_t byte; // Unsigned 8 bits.
typedef uint16_t ushort; // Preferably 16 bits, but can be more.
typedef unsigned int uint; // Preferably 32 bits, likely can be more.
typedef uint32_t uint32; // 32 bits exactly.
typedef int32_t int32; // Signed 32 bits exactly.
typedef uint64_t uint64; // 64 bits exactly.
typedef int64_t int64; // Signed 64 bits exactly.
typedef wchar_t wchar; // Unicode character
// Get lowest 16 bits.
#define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff))
// Make 64 bit integer from two 32 bit.
#define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low))
// Maximum int64 value.
#define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff))
// Special int64 value, large enough to never be found in real life
// and small enough to fit to both signed and unsigned 64-bit ints.
// We use it in situations, when we need to indicate that parameter
// is not defined and probably should be calculated inside of function.
// Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it
// compatible with 32 bit int64 if 64 bit type is not supported.
#define INT64NDF INT32TO64(0x7fffffff,0x7fffffff)
#endif
+364
View File
@@ -0,0 +1,364 @@
#include "rar.hpp"
RarVM::RarVM()
{
Mem=NULL;
}
RarVM::~RarVM()
{
delete[] Mem;
}
void RarVM::Init()
{
if (Mem==NULL)
Mem=new byte[VM_MEMSIZE+4];
}
void RarVM::Execute(VM_PreparedProgram *Prg)
{
memcpy(R,Prg->InitR,sizeof(Prg->InitR));
Prg->FilteredData=NULL;
if (Prg->Type!=VMSF_NONE)
{
bool Success=ExecuteStandardFilter(Prg->Type);
uint BlockSize=Prg->InitR[4] & VM_MEMMASK;
Prg->FilteredDataSize=BlockSize;
if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO)
Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize;
else
Prg->FilteredData=Mem;
}
}
void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg)
{
// Calculate the single byte XOR checksum to check validity of VM code.
byte XorSum=0;
for (uint I=1;I<CodeSize;I++)
XorSum^=Code[I];
if (XorSum!=Code[0])
return;
struct StandardFilters
{
uint Length;
uint CRC;
VM_StandardFilters Type;
} static StdList[]={
53, 0xad576887, VMSF_E8,
57, 0x3cd7e57e, VMSF_E8E9,
120, 0x3769893f, VMSF_ITANIUM,
29, 0x0e06077d, VMSF_DELTA,
149, 0x1c2c5dc8, VMSF_RGB,
216, 0xbc85e701, VMSF_AUDIO
};
uint CodeCRC=CRC32(0xffffffff,Code,CodeSize)^0xffffffff;
for (uint I=0;I<ASIZE(StdList);I++)
if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize)
{
Prg->Type=StdList[I].Type;
break;
}
}
uint RarVM::ReadData(BitInput &Inp)
{
uint Data=Inp.fgetbits();
switch(Data&0xc000)
{
case 0:
Inp.faddbits(6);
return (Data>>10)&0xf;
case 0x4000:
if ((Data&0x3c00)==0)
{
Data=0xffffff00|((Data>>2)&0xff);
Inp.faddbits(14);
}
else
{
Data=(Data>>6)&0xff;
Inp.faddbits(10);
}
return Data;
case 0x8000:
Inp.faddbits(2);
Data=Inp.fgetbits();
Inp.faddbits(16);
return Data;
default:
Inp.faddbits(2);
Data=(Inp.fgetbits()<<16);
Inp.faddbits(16);
Data|=Inp.fgetbits();
Inp.faddbits(16);
return Data;
}
}
void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize)
{
if (Pos<VM_MEMSIZE && Data!=Mem+Pos)
{
// We can have NULL Data for invalid filters with DataSize==0. While most
// sensible memmove implementations do not care about data if size is 0,
// let's follow the standard and check the size first.
size_t CopySize=Min(DataSize,VM_MEMSIZE-Pos);
if (CopySize!=0)
memmove(Mem+Pos,Data,CopySize);
}
}
bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType)
{
switch(FilterType)
{
case VMSF_E8:
case VMSF_E8E9:
{
byte *Data=Mem;
uint DataSize=R[4],FileOffset=R[6];
if (DataSize>VM_MEMSIZE || DataSize<4)
return false;
const uint FileSize=0x1000000;
byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8;
for (uint CurPos=0;CurPos<DataSize-4;)
{
byte CurByte=*(Data++);
CurPos++;
if (CurByte==0xe8 || CurByte==CmpByte2)
{
uint Offset=CurPos+FileOffset;
uint Addr=RawGet4(Data);
// We check 0x80000000 bit instead of '< 0' comparison
// not assuming int32 presence or uint size and endianness.
if ((Addr & 0x80000000)!=0) // Addr<0
{
if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0
RawPut4(Addr+FileSize,Data);
}
else
if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize
RawPut4(Addr-Offset,Data);
Data+=4;
CurPos+=4;
}
}
}
break;
case VMSF_ITANIUM:
{
byte *Data=Mem;
uint DataSize=R[4],FileOffset=R[6];
if (DataSize>VM_MEMSIZE || DataSize<21)
return false;
uint CurPos=0;
FileOffset>>=4;
while (CurPos<DataSize-21)
{
int Byte=(Data[0]&0x1f)-0x10;
if (Byte>=0)
{
static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};
byte CmdMask=Masks[Byte];
if (CmdMask!=0)
for (uint I=0;I<=2;I++)
if (CmdMask & (1<<I))
{
uint StartPos=I*41+5;
uint OpType=FilterItanium_GetBits(Data,StartPos+37,4);
if (OpType==5)
{
uint Offset=FilterItanium_GetBits(Data,StartPos+13,20);
FilterItanium_SetBits(Data,(Offset-FileOffset)&0xfffff,StartPos+13,20);
}
}
}
Data+=16;
CurPos+=16;
FileOffset++;
}
}
break;
case VMSF_DELTA:
{
uint DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2;
if (DataSize>VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0)
return false;
// Bytes from same channels are grouped to continual data blocks,
// so we need to place them back to their interleaving positions.
for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
byte PrevByte=0;
for (uint DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels)
Mem[DestPos]=(PrevByte-=Mem[SrcPos++]);
}
}
break;
case VMSF_RGB:
{
uint DataSize=R[4],Width=R[0]-3,PosR=R[1];
if (DataSize>VM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2)
return false;
byte *SrcData=Mem,*DestData=SrcData+DataSize;
const uint Channels=3;
for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
uint PrevByte=0;
for (uint I=CurChannel;I<DataSize;I+=Channels)
{
uint Predicted;
if (I>=Width+3)
{
byte *UpperData=DestData+I-Width;
uint UpperByte=*UpperData;
uint UpperLeftByte=*(UpperData-3);
Predicted=PrevByte+UpperByte-UpperLeftByte;
int pa=abs((int)(Predicted-PrevByte));
int pb=abs((int)(Predicted-UpperByte));
int pc=abs((int)(Predicted-UpperLeftByte));
if (pa<=pb && pa<=pc)
Predicted=PrevByte;
else
if (pb<=pc)
Predicted=UpperByte;
else
Predicted=UpperLeftByte;
}
else
Predicted=PrevByte;
PrevByte=DestData[I]=(byte)(Predicted-*(SrcData++));
}
}
for (uint I=PosR,Border=DataSize-2;I<Border;I+=3)
{
byte G=DestData[I+1];
DestData[I]+=G;
DestData[I+2]+=G;
}
}
break;
case VMSF_AUDIO:
{
uint DataSize=R[4],Channels=R[0];
byte *SrcData=Mem,*DestData=SrcData+DataSize;
// In fact, audio channels never exceed 4.
if (DataSize>VM_MEMSIZE/2 || Channels>128 || Channels==0)
return false;
for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
{
uint PrevByte=0,PrevDelta=0,Dif[7];
int D1=0,D2=0,D3;
int K1=0,K2=0,K3=0;
memset(Dif,0,sizeof(Dif));
for (uint I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++)
{
D3=D2;
D2=PrevDelta-D1;
D1=PrevDelta;
uint Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3;
Predicted=(Predicted>>3) & 0xff;
uint CurByte=*(SrcData++);
Predicted-=CurByte;
DestData[I]=Predicted;
PrevDelta=(signed char)(Predicted-PrevByte);
PrevByte=Predicted;
int D=(signed char)CurByte;
// Left shift of negative value is undefined behavior in C++,
// so we cast it to unsigned to follow the standard.
D=(uint)D<<3;
Dif[0]+=abs(D);
Dif[1]+=abs(D-D1);
Dif[2]+=abs(D+D1);
Dif[3]+=abs(D-D2);
Dif[4]+=abs(D+D2);
Dif[5]+=abs(D-D3);
Dif[6]+=abs(D+D3);
if ((ByteCount & 0x1f)==0)
{
uint MinDif=Dif[0],NumMinDif=0;
Dif[0]=0;
for (uint J=1;J<ASIZE(Dif);J++)
{
if (Dif[J]<MinDif)
{
MinDif=Dif[J];
NumMinDif=J;
}
Dif[J]=0;
}
switch(NumMinDif)
{
case 1: if (K1>=-16) K1--; break;
case 2: if (K1 < 16) K1++; break;
case 3: if (K2>=-16) K2--; break;
case 4: if (K2 < 16) K2++; break;
case 5: if (K3>=-16) K3--; break;
case 6: if (K3 < 16) K3++; break;
}
}
}
}
}
break;
}
return true;
}
uint RarVM::FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount)
{
uint InAddr=BitPos/8;
uint InBit=BitPos&7;
uint BitField=(uint)Data[InAddr++];
BitField|=(uint)Data[InAddr++] << 8;
BitField|=(uint)Data[InAddr++] << 16;
BitField|=(uint)Data[InAddr] << 24;
BitField >>= InBit;
return BitField & (0xffffffff>>(32-BitCount));
}
void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount)
{
uint InAddr=BitPos/8;
uint InBit=BitPos&7;
uint AndMask=0xffffffff>>(32-BitCount);
AndMask=~(AndMask<<InBit);
BitField<<=InBit;
for (uint I=0;I<4;I++)
{
Data[InAddr+I]&=AndMask;
Data[InAddr+I]|=BitField;
AndMask=(AndMask>>8)|0xff000000;
BitField>>=8;
}
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef _RAR_VM_
#define _RAR_VM_
#define VM_MEMSIZE 0x40000
#define VM_MEMMASK (VM_MEMSIZE-1)
enum VM_StandardFilters {
VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO,
VMSF_DELTA
};
struct VM_PreparedProgram
{
VM_PreparedProgram()
{
FilteredDataSize=0;
Type=VMSF_NONE;
}
VM_StandardFilters Type;
uint InitR[7];
byte *FilteredData;
uint FilteredDataSize;
};
class RarVM
{
private:
bool ExecuteStandardFilter(VM_StandardFilters FilterType);
uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount);
void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount);
byte *Mem;
uint R[8];
public:
RarVM();
~RarVM();
void Init();
void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg);
void Execute(VM_PreparedProgram *Prg);
void SetMemory(size_t Pos,byte *Data,size_t DataSize);
static uint ReadData(BitInput &Inp);
};
#endif
+181
View File
@@ -0,0 +1,181 @@
#ifndef _RAR_RAWINT_
#define _RAR_RAWINT_
#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
#define rotl32(x,n) rotls(x,n,32)
#define rotr32(x,n) rotrs(x,n,32)
inline uint RawGet2(const void *Data)
{
byte *D=(byte *)Data;
return D[0]+(D[1]<<8);
}
inline uint32 RawGet4(const void *Data)
{
#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
byte *D=(byte *)Data;
return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24);
#else
return *(uint32 *)Data;
#endif
}
inline uint64 RawGet8(const void *Data)
{
#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
byte *D=(byte *)Data;
return INT32TO64(RawGet4(D+4),RawGet4(D));
#else
return *(uint64 *)Data;
#endif
}
inline void RawPut2(uint Field,void *Data)
{
byte *D=(byte *)Data;
D[0]=(byte)(Field);
D[1]=(byte)(Field>>8);
}
inline void RawPut4(uint Field,void *Data)
{
#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
byte *D=(byte *)Data;
D[0]=(byte)(Field);
D[1]=(byte)(Field>>8);
D[2]=(byte)(Field>>16);
D[3]=(byte)(Field>>24);
#else
*(uint32 *)Data=(uint32)Field;
#endif
}
inline void RawPut8(uint64 Field,void *Data)
{
#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
byte *D=(byte *)Data;
D[0]=(byte)(Field);
D[1]=(byte)(Field>>8);
D[2]=(byte)(Field>>16);
D[3]=(byte)(Field>>24);
D[4]=(byte)(Field>>32);
D[5]=(byte)(Field>>40);
D[6]=(byte)(Field>>48);
D[7]=(byte)(Field>>56);
#else
*(uint64 *)Data=Field;
#endif
}
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
#define USE_MEM_BYTESWAP
#endif
// Load 4 big endian bytes from memory and return uint32.
inline uint32 RawGetBE4(const byte *m)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_ulong(*(uint32 *)m);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
return __builtin_bswap32(*(uint32 *)m);
#else
return uint32(m[0])<<24 | uint32(m[1])<<16 | uint32(m[2])<<8 | m[3];
#endif
}
// Load 8 big endian bytes from memory and return uint64.
inline uint64 RawGetBE8(const byte *m)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
return _byteswap_uint64(*(uint64 *)m);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
return __builtin_bswap64(*(uint64 *)m);
#else
return uint64(m[0])<<56 | uint64(m[1])<<48 | uint64(m[2])<<40 | uint64(m[3])<<32 |
uint64(m[4])<<24 | uint64(m[5])<<16 | uint64(m[6])<<8 | m[7];
#endif
}
// Save integer to memory as big endian.
inline void RawPutBE4(uint i,byte *mem)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint32*)mem = _byteswap_ulong((uint32)i);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
*(uint32*)mem = __builtin_bswap32((uint32)i);
#else
mem[0]=byte(i>>24);
mem[1]=byte(i>>16);
mem[2]=byte(i>>8);
mem[3]=byte(i);
#endif
}
// Save integer to memory as big endian.
inline void RawPutBE8(uint64 i,byte *mem)
{
#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
*(uint64*)mem = _byteswap_uint64(i);
#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__))
*(uint64*)mem = __builtin_bswap64(i);
#else
mem[0]=byte(i>>56);
mem[1]=byte(i>>48);
mem[2]=byte(i>>40);
mem[3]=byte(i>>32);
mem[4]=byte(i>>24);
mem[5]=byte(i>>16);
mem[6]=byte(i>>8);
mem[7]=byte(i);
#endif
}
inline uint32 ByteSwap32(uint32 i)
{
#ifdef _MSC_VER
return _byteswap_ulong(i);
#elif defined(__clang__) || defined(__GNUC__)
return __builtin_bswap32(i);
#else
return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);
#endif
}
inline bool IsPow2(uint64 n) // Check if 'n' is power of 2.
{
return (n & (n-1))==0;
}
inline uint64 GetGreaterOrEqualPow2(uint64 n)
{
uint64 p=1;
while (p<n)
p*=2;
return p;
}
inline uint64 GetLessOrEqualPow2(uint64 n)
{
uint64 p=1;
while (p*2<=n)
p*=2;
return p;
}
#endif
+207
View File
@@ -0,0 +1,207 @@
#include "rar.hpp"
RawRead::RawRead()
{
RawRead::SrcFile=nullptr;
Reset();
}
RawRead::RawRead(File *SrcFile)
{
RawRead::SrcFile=SrcFile;
Reset();
}
void RawRead::Reset()
{
Data.clear();
ReadPos=0;
DataSize=0;
Crypt=NULL;
}
size_t RawRead::Read(size_t Size)
{
size_t ReadSize=0;
#if !defined(RAR_NOCRYPT)
if (Crypt!=NULL)
{
// Full size of buffer with already read data including data read
// for encryption block alignment.
size_t FullSize=Data.size();
// Data read for alignment and not processed yet.
size_t DataLeft=FullSize-DataSize;
if (Size>DataLeft) // Need to read more than we already have.
{
size_t SizeToRead=Size-DataLeft;
size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK);
Data.resize(FullSize+AlignedReadSize);
ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize);
Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize);
DataSize+=ReadSize==0 ? 0:Size;
}
else // Use buffered data, no real read.
{
ReadSize=Size;
DataSize+=Size;
}
}
else
#endif
if (Size!=0)
{
Data.resize(Data.size()+Size);
ReadSize=SrcFile->Read(&Data[DataSize],Size);
DataSize+=ReadSize;
}
return ReadSize;
}
void RawRead::Read(byte *SrcData,size_t Size)
{
if (Size!=0)
{
Data.resize(Data.size()+Size);
memcpy(&Data[DataSize],SrcData,Size);
DataSize+=Size;
}
}
// Move unread data to beginning of buffer and adjust buffer size.
void RawRead::Compact()
{
if (ReadPos<DataSize) // We would access beyond the vector end otherwise.
memmove(&Data[0],&Data[ReadPos],DataSize-ReadPos);
DataSize-=ReadPos;
ReadPos=0;
Data.resize(DataSize);
}
byte RawRead::Get1()
{
return ReadPos<DataSize ? Data[ReadPos++]:0;
}
ushort RawRead::Get2()
{
if (ReadPos+1<DataSize)
{
ushort Result=Data[ReadPos]+(Data[ReadPos+1]<<8);
ReadPos+=2;
return Result;
}
return 0;
}
uint RawRead::Get4()
{
if (ReadPos+3<DataSize)
{
uint Result=RawGet4(&Data[ReadPos]);
ReadPos+=4;
return Result;
}
return 0;
}
uint64 RawRead::Get8()
{
uint Low=Get4(),High=Get4();
return INT32TO64(High,Low);
}
uint64 RawRead::GetV()
{
uint64 Result=0;
// Need to check Shift<64, because for shift greater than or equal to
// the width of the promoted left operand, the behavior is undefined.
for (uint Shift=0;ReadPos<DataSize && Shift<64;Shift+=7)
{
byte CurByte=Data[ReadPos++];
Result+=uint64(CurByte & 0x7f)<<Shift;
if ((CurByte & 0x80)==0)
return Result; // Decoded successfully.
}
return 0; // Out of buffer border.
}
// Return a number of bytes in current variable length integer.
uint RawRead::GetVSize(size_t Pos)
{
for (size_t CurPos=Pos;CurPos<DataSize;CurPos++)
if ((Data[CurPos] & 0x80)==0)
return int(CurPos-Pos+1);
return 0; // Buffer overflow.
}
size_t RawRead::GetB(void *Field,size_t Size)
{
byte *F=(byte *)Field;
size_t CopySize=Min(DataSize-ReadPos,Size);
if (CopySize>0)
memcpy(F,&Data[ReadPos],CopySize);
if (Size>CopySize)
memset(F+CopySize,0,Size-CopySize);
ReadPos+=CopySize;
return CopySize;
}
void RawRead::GetW(wchar *Field,size_t Size)
{
if (ReadPos+2*Size-1<DataSize)
{
RawToWide(&Data[ReadPos],Field,Size);
ReadPos+=sizeof(wchar)*Size;
}
else
memset(Field,0,sizeof(wchar)*Size);
}
uint RawRead::GetCRC15(bool ProcessedOnly) // RAR 1.5 block CRC.
{
if (DataSize<=2)
return 0;
uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2);
return ~HeaderCRC & 0xffff;
}
uint RawRead::GetCRC50() // RAR 5.0 block CRC.
{
if (DataSize<=4)
return 0xffffffff;
return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff;
}
// Read vint from arbitrary byte array.
uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow)
{
Overflow=false;
uint64 Result=0;
for (uint Shift=0;ReadPos<DataSize;Shift+=7)
{
byte CurByte=Data[ReadPos++];
Result+=uint64(CurByte & 0x7f)<<Shift;
if ((CurByte & 0x80)==0)
return Result; // Decoded successfully.
}
Overflow=true;
return 0; // Out of buffer border.
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef _RAR_RAWREAD_
#define _RAR_RAWREAD_
class RawRead
{
private:
std::vector<byte> Data;
File *SrcFile;
size_t DataSize;
size_t ReadPos;
CryptData *Crypt;
public:
RawRead();
RawRead(File *SrcFile);
void Reset();
size_t Read(size_t Size);
void Read(byte *SrcData,size_t Size);
void Compact();
byte Get1();
ushort Get2();
uint Get4();
uint64 Get8();
uint64 GetV();
uint GetVSize(size_t Pos);
size_t GetB(void *Field,size_t Size);
void GetW(wchar *Field,size_t Size);
uint GetCRC15(bool ProcessedOnly);
uint GetCRC50();
byte* GetDataPtr() {return &Data[0];}
size_t Size() {return DataSize;}
size_t PaddedSize() {return Data.size()-DataSize;}
size_t DataLeft() {return DataSize-ReadPos;}
size_t GetPos() {return ReadPos;}
void SetPos(size_t Pos) {ReadPos=Pos;}
void Skip(size_t Size) {ReadPos+=Size;}
void Rewind() {SetPos(0);}
void SetCrypt(CryptData *Crypt) {RawRead::Crypt=Crypt;}
};
uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow);
#endif
+326
View File
@@ -0,0 +1,326 @@
#include "rar.hpp"
ComprDataIO::ComprDataIO()
{
#ifndef RAR_NOCRYPT
Crypt=new CryptData;
Decrypt=new CryptData;
#endif
Init();
}
void ComprDataIO::Init()
{
UnpackFromMemory=false;
UnpackToMemory=false;
UnpPackedSize=0;
UnpPackedLeft=0;
ShowProgress=true;
TestMode=false;
SkipUnpCRC=false;
NoFileHeader=false;
PackVolume=false;
UnpVolume=false;
NextVolumeMissing=false;
SrcFile=NULL;
DestFile=NULL;
UnpWrAddr=NULL;
UnpWrSize=0;
Command=NULL;
Encryption=false;
Decryption=false;
CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0;
LastPercent=-1;
SubHead=NULL;
SubHeadPos=NULL;
CurrentCommand=0;
ProcessedArcSize=0;
LastArcSize=0;
TotalArcSize=0;
}
ComprDataIO::~ComprDataIO()
{
#ifndef RAR_NOCRYPT
delete Crypt;
delete Decrypt;
#endif
}
int ComprDataIO::UnpRead(byte *Addr,size_t Count)
{
#ifndef RAR_NOCRYPT
// In case of encryption we need to align read size to encryption
// block size. We can do it by simple masking, because unpack read code
// always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0.
if (Decryption)
Count &= ~CRYPT_BLOCK_MASK;
#endif
int ReadSize=0,TotalRead=0;
byte *ReadAddr;
ReadAddr=Addr;
while (Count > 0)
{
Archive *SrcArc=(Archive *)SrcFile;
if (UnpackFromMemory)
{
memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize);
ReadSize=(int)UnpackFromMemorySize;
UnpackFromMemorySize=0;
}
else
{
size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count;
if (SizeToRead > 0)
{
if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft)
{
// We need aligned blocks for decryption and we want "Keep broken
// files" to work efficiently with missing encrypted volumes.
// So for last data block in volume we adjust the size to read to
// next equal or smaller block producing aligned total block size.
// So we'll ask for next volume only when processing few unaligned
// bytes left in the end, when most of data is already extracted.
size_t NewTotalRead = TotalRead + SizeToRead;
size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK);
size_t NewSizeToRead = SizeToRead - Adjust;
if ((int)NewSizeToRead > 0)
SizeToRead = NewSizeToRead;
}
if (!SrcFile->IsOpened())
return -1;
ReadSize=SrcFile->Read(ReadAddr,SizeToRead);
FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead;
if (!NoFileHeader && hd->SplitAfter)
PackedDataHash.Update(ReadAddr,ReadSize);
}
}
CurUnpRead+=ReadSize;
TotalRead+=ReadSize;
#ifndef NOVOLUME
// These variable are not used in NOVOLUME mode, so it is better
// to exclude commands below to avoid compiler warnings.
ReadAddr+=ReadSize;
Count-=ReadSize;
#endif
UnpPackedLeft-=ReadSize;
// Do not ask for next volume if we read something from current volume.
// If next volume is missing, we need to process all data from current
// volume before aborting. It helps to recover all possible data
// in "Keep broken files" mode. But if we process encrypted data,
// we ask for next volume also if we have non-aligned encryption block.
// Since we adjust data size for decryption earlier above,
// it does not hurt "Keep broken files" mode efficiency.
if (UnpVolume && UnpPackedLeft == 0 &&
(ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) )
{
#ifndef NOVOLUME
if (!MergeArchive(*SrcArc,this,true,CurrentCommand))
#endif
{
NextVolumeMissing=true;
return -1;
}
}
else
break;
}
Archive *SrcArc=(Archive *)SrcFile;
if (SrcArc!=NULL)
ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize);
if (ReadSize!=-1)
{
ReadSize=TotalRead;
#ifndef RAR_NOCRYPT
if (Decryption)
Decrypt->DecryptBlock(Addr,ReadSize);
#endif
}
Wait();
return ReadSize;
}
void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#ifdef RARDLL
CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1)
ErrHandler.Exit(RARX_USERBREAK);
if (Cmd->ProcessDataProc!=NULL)
{
int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
if (RetCode==0)
ErrHandler.Exit(RARX_USERBREAK);
}
}
#endif // RARDLL
UnpWrAddr=Addr;
UnpWrSize=Count;
if (UnpackToMemory)
{
if (Count <= UnpackToMemorySize)
{
memcpy(UnpackToMemoryAddr,Addr,Count);
UnpackToMemoryAddr+=Count;
UnpackToMemorySize-=Count;
}
}
else
if (!TestMode)
DestFile->Write(Addr,Count);
CurUnpWrite+=Count;
if (!SkipUnpCRC)
UnpHash.Update(Addr,Count);
ShowUnpWrite();
Wait();
}
void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
{
if (ShowProgress && SrcFile!=NULL)
{
// Important when processing several archives or multivolume archive.
ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
{
uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize);
LastPercent=CurPercent;
}
}
}
void ComprDataIO::ShowUnpWrite()
{
}
void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
{
if (SrcFile!=NULL)
ComprDataIO::SrcFile=SrcFile;
if (DestFile!=NULL)
ComprDataIO::DestFile=DestFile;
LastPercent=-1;
}
void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
{
*Data=UnpWrAddr;
*Size=UnpWrSize;
}
// Return true if encryption or decryption mode is set correctly.
bool ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,const byte *InitV,
uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
#ifdef RAR_NOCRYPT
return false;
#else
if (Encrypt)
{
Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Encryption;
}
else
{
Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Decryption;
}
#endif
}
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetCmt13Encryption()
{
Decryption=true;
Decrypt->SetCmt13Encryption();
}
#endif
void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
{
UnpackToMemory=true;
UnpackToMemoryAddr=Addr;
UnpackToMemorySize=Size;
}
// Extraction progress is based on the position in archive and we adjust
// the total archives size here, so trailing blocks do not prevent progress
// reaching 100% at the end of extraction. Alternatively we could print "100%"
// after completing the entire archive extraction, but then we would need
// to take into account possible messages like the checksum error after
// last file percent progress.
void ComprDataIO::AdjustTotalArcSize(Archive *Arc)
{
// If we know a position of QO or RR blocks, use them to adjust the total
// packed size to beginning of these blocks. Earlier we already calculated
// the total size based on entire archive sizes. We also set LastArcSize
// to start of first trailing block, to add it later to ProcessedArcSize.
uint64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0;
// QO is always preceding RR record.
// Also we check QO and RR to be less than archive length to prevent
// negative "ArcLength-LastArcSize" and possible signed integer overflow
// when calculating TotalArcSize.
if (Arc->MainHead.QOpenOffset>0 && Arc->MainHead.QOpenOffset<ArcLength)
LastArcSize=Arc->MainHead.QOpenOffset;
else
if (Arc->MainHead.RROffset>0 && Arc->MainHead.RROffset<ArcLength)
LastArcSize=Arc->MainHead.RROffset;
else
{
// If neither QO nor RR are found, exclude the approximate size of
// end of archive block.
// We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC,
// but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file
// name, so we do not have two files with 100% at the end of archive.
const uint EndBlock=23;
if (ArcLength>EndBlock)
LastArcSize=ArcLength-EndBlock;
}
TotalArcSize-=ArcLength-LastArcSize;
}
+100
View File
@@ -0,0 +1,100 @@
#ifndef _RAR_DATAIO_
#define _RAR_DATAIO_
class Archive;
class CmdAdd;
class Unpack;
class ArcFileSearch;
class ComprDataIO
{
private:
void ShowUnpRead(int64 ArcPos,int64 ArcSize);
void ShowUnpWrite();
bool UnpackFromMemory;
size_t UnpackFromMemorySize;
byte *UnpackFromMemoryAddr;
bool UnpackToMemory;
size_t UnpackToMemorySize;
byte *UnpackToMemoryAddr;
size_t UnpWrSize;
byte *UnpWrAddr;
int64 UnpPackedSize;
int64 UnpPackedLeft;
bool ShowProgress;
bool TestMode;
bool SkipUnpCRC;
bool NoFileHeader;
File *SrcFile;
File *DestFile;
CmdAdd *Command;
FileHeader *SubHead;
int64 *SubHeadPos;
#ifndef RAR_NOCRYPT
CryptData *Crypt;
CryptData *Decrypt;
#endif
int LastPercent;
wchar CurrentCommand;
public:
ComprDataIO();
~ComprDataIO();
void Init();
int UnpRead(byte *Addr,size_t Count);
void UnpWrite(byte *Addr,size_t Count);
void EnableShowProgress(bool Show) {ShowProgress=Show;}
void GetUnpackedData(byte **Data,size_t *Size);
void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;}
void SetTestMode(bool Mode) {TestMode=Mode;}
void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;}
void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;}
void SetFiles(File *SrcFile,File *DestFile);
void SetCommand(CmdAdd *Cmd) {Command=Cmd;}
void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;}
bool SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
void SetCmt13Encryption();
void SetUnpackToMemory(byte *Addr,uint Size);
void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;}
void AdjustTotalArcSize(Archive *Arc);
bool PackVolume;
bool UnpVolume;
bool NextVolumeMissing;
int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite;
// Size of already processed archives.
// Used to calculate the total operation progress.
int64 ProcessedArcSize;
// Last extracted archive size up to QO or RR block.
int64 LastArcSize;
int64 TotalArcSize;
DataHash PackedDataHash; // Packed write and unpack read hash.
DataHash PackHash; // Pack read hash.
DataHash UnpHash; // Unpack write hash.
bool Encryption;
bool Decryption;
};
#endif
Loaded 100 of 161 files, more files were not shown because too many files have changed in this diff. Show more