16 Commits
master ... v4.x

Author SHA1 Message Date
macintoshplus
38fe9a67c4 add Funding 2026-02-25 12:40:55 +01:00
Jean-Baptiste Nahan
1a623543c4 Merge pull request #2 from php-win-ext/clean_repo
📝 clean repo and add some informations
2026-02-25 09:32:08 +01:00
macintoshplus
cfffb63b5a 📝 clean repo and add some informations 2026-02-25 00:48:39 +01:00
Jean-Baptiste Nahan
c99b6925ad Merge pull request #1 from php-win-ext/add_build_release
Update Code and Tests
2026-02-25 00:37:02 +01:00
macintoshplus
792c622f3e remove php 8.1 limitation for Windows test 2026-02-25 00:28:11 +01:00
macintoshplus
3f507ce2db update test for Windows x86 platform 2026-02-25 00:25:46 +01:00
macintoshplus
7ba9c28e15 try fix test 092 on Windows 2026-02-25 00:05:22 +01:00
macintoshplus
717d75ec8e add dump in test for debug test on windows 2026-02-24 23:49:33 +01:00
macintoshplus
498286b7af 👷 fix Windows test 041 fail 2026-02-24 23:35:48 +01:00
macintoshplus
d1c7fcd5a1 👷 fix test for Windows 2026-02-24 23:33:42 +01:00
macintoshplus
55d30dfa5d enable window CI for branches 2026-02-24 23:23:26 +01:00
macintoshplus
52f8a34342 🔧 update gitignore 2026-02-24 23:20:57 +01:00
macintoshplus
5a930a03aa 💚 fix CI PHP TS value 2026-02-24 23:18:14 +01:00
macintoshplus
234a78c2f7 💚 fix CI for PHP 8.5 2026-02-24 23:15:05 +01:00
macintoshplus
19b94313c2 update code for PHP 8.2+ 2026-02-24 22:48:56 +01:00
macintoshplus
7b2749af57 Add GitHub workflow and PIE Configuration 2026-02-24 22:11:42 +01:00
192 changed files with 5641 additions and 9097 deletions

View File

@@ -1,88 +0,0 @@
---
# Formatting style for php-rar C source files.
# Does NOT apply to unrar/ (see unrar/.clang-format).
Language: Cpp
BasedOnStyle: LLVM
# Indentation: real tabs, width 4 (matches vim modeline: noet sw=4 ts=4)
UseTab: Always
TabWidth: 4
IndentWidth: 4
ContinuationIndentWidth: 4
# Disable line-length wrapping to preserve hand-formatted long lines
ColumnLimit: 0
# Brace style:
# - Functions: brace on its own line (Allman)
# - Structs/unions/enums/extern "C": brace attached (K&R)
# - Control statements: brace attached (K&R)
# - else/else if: on its own line (after closing })
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
# Preserve indentation of preprocessor directives (# include, # define inside #ifdef)
IndentPPDirectives: AfterHash
PPIndentWidth: 1
# Pointer star attached to variable name: char *ptr
PointerAlignment: Right
DerivePointerAlignment: false
# Spaces
SpaceBeforeParens: ControlStatements
SpaceAfterCStyleCast: true
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpaceBeforeAssignmentOperators: true
# Don't align backslashes in multi-line macros (just 1 space before \)
AlignEscapedNewlines: DontAlign
# ZEND_BEGIN/END_ARG_INFO_EX act as block delimiters so ZEND_ARG_INFO lines
# inside them keep their indentation.
MacroBlockBegin: "^ZEND_BEGIN_ARG_INFO"
MacroBlockEnd: "^ZEND_END_ARG_INFO"
# Align continuation arguments after opening paren; when ( is last on the line,
# fall back to a block indent (ContinuationIndentWidth).
AlignAfterOpenBracket: Align
# Preserve existing include order
SortIncludes: Never
# case labels are indented one level inside switch
IndentCaseLabels: true
# Don't collapse or expand short constructs
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
# Alignment: leave hand-aligned blocks alone
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignTrailingComments: false
# Blank lines
MaxEmptyLinesToKeep: 2
KeepEmptyLinesAtTheStartOfBlocks: true

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [macintoshplus] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,27 +0,0 @@
# Docker image SHA lock file.
# Maps image tags to their multi-arch OCI index digests (amd64 + arm64).
# Regenerate with: .github/scripts/update-docker-shas.sh
datadog/dd-appsec-php-ci:
php-7.0-debug: "sha256:0a9aaeaf79bd4c578eac6dedabf6ac131537266f44562158ce67fdb37c794916"
php-7.0-release-zts: "sha256:4bb5fac6fbd3124234d062328c68f6c51027aa95f13e4e8b14afd7c8de518ec6"
php-7.1-debug: "sha256:500007ad057d9e71b2bb9079a2f8ba3f1ecfbb26d112db69e17d6007b4e857b5"
php-7.1-release-zts: "sha256:d997b9f99c28967872bd0949f8572b089daaadb6ceaa1e856ccc76e07e2ba6b7"
php-7.2-debug: "sha256:4c5f111f8e84fcb7dcc9e3cc13e1277d0702b04cd33286ce33db885485d1f025"
php-7.2-release-zts: "sha256:634b857d74c3d77b88986ceb088be2dd5bc30151bc08c2b536443984e6659d6e"
php-7.3-debug: "sha256:efa81f79783097478a434578226fe9a3b8fe84abda33168034aaea60c197c73b"
php-7.3-release-zts: "sha256:c713df299596a9615f88cfe73c29b0a1f9faf32e5e6fa62fa07ee839313cd57e"
php-7.4-debug: "sha256:b8a9e982179189122d73feb896c1a1e8578a92fc9a023dabc825f45db8299c22"
php-7.4-release-zts: "sha256:6492e3334e722b106352180ec9f0cbee8dd81f008e3537d03f4b8da3522f49e1"
php-8.0-debug: "sha256:900ceae7487db1e3652de2880c181e572fdf053673bcda8ff47abf664ff74d39"
php-8.0-release-zts: "sha256:b6243199f6aea0792a97583c9036f0b191ad9efb96ea337632fbaca76289a4da"
php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6"
php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8"
php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5"
php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c"
php-8.3-debug: "sha256:bb6df08160126374d3d9247428928aa19a9c2b2429c98356650199b85ae20212"
php-8.3-release-zts: "sha256:e58e25a017f75df82691d408b8cb70453875ff36718e295ee8c6653a0f117331"
php-8.4-debug: "sha256:15045688f6986f4625b1507a7f4be6104e7bbb88caf877f1611463b929f2bca2"
php-8.4-release-zts: "sha256:8e0ac25a3306b4b9f692c593b8a509cc789c2e001ce52682928065a92c880136"
php-8.5-debug: "sha256:bd0170331b34fb469e29d00b19b20fb88b726160f76df274a1bdc3a27ac18d30"
php-8.5-release-zts: "sha256:e071b2095da55bd24686209422f43a01c65acfc6021f04156d9fb43fd3d4d426"

View File

@@ -1,35 +0,0 @@
#!/usr/bin/env bash
# Build the extension and run the test suite.
# Expected to run inside a datadog/dd-appsec-php-ci container from the repo root.
set -euo pipefail
# Clean up artifacts from any previous build so stale objects don't survive a
# PHP-version switch (safe in CI where the workspace is always fresh).
if [ -f Makefile ]; then
make -f Makefile distclean
fi
phpize
./configure --with-php-config="$(which php-config)"
make -f Makefile -j"$(nproc)"
# The generated Makefile silences and ignores errors on the `if` commands;
# undo that so test failures surface properly.
sed -i 's/-@if/@if/' Makefile
ret=0
TEST_PHP_EXECUTABLE="$(which php)" \
TEST_PHP_JUNIT=report.xml \
REPORT_EXIT_STATUS=1 \
NO_INTERACTION=1 \
TESTS="--set-timeout 300 --show-diff" \
make -f Makefile test || ret=$?
found=$(find tests -name '*.mem' | wc -l)
if [ "$found" -gt 0 ]; then
echo "Found $found memory leak(s):"
find tests -name '*.mem' -print -exec cat {} \;
ret=1
fi
exit "$ret"

View File

@@ -1,52 +0,0 @@
#!/usr/bin/env bash
# Refresh docker-image-shas.yml with current OCI index digests.
# The index digest covers all platforms (amd64 + arm64); Docker resolves the
# right platform image from it at pull time.
# Justfile and tests.yml read from docker-image-shas.yml directly — no patching needed.
set -euo pipefail
IMAGE="datadog/dd-appsec-php-ci"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCK_FILE="$SCRIPT_DIR/../docker-image-shas.yml"
TAGS=(
php-7.0-debug php-7.0-release-zts
php-7.1-debug php-7.1-release-zts
php-7.2-debug php-7.2-release-zts
php-7.3-debug php-7.3-release-zts
php-7.4-debug php-7.4-release-zts
php-8.0-debug php-8.0-release-zts
php-8.1-debug php-8.1-release-zts
php-8.2-debug php-8.2-release-zts
php-8.3-debug php-8.3-release-zts
php-8.4-debug php-8.4-release-zts
php-8.5-debug php-8.5-release-zts
)
get_index_digest() {
# The top-level "digest" field in the Hub tags API is the manifest-list
# (OCI index) digest, not a per-platform image digest.
curl -fsSL "https://hub.docker.com/v2/repositories/${IMAGE}/tags/$1" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
}
# Collect all digests first so we fail fast before touching any file.
declare -A DIGESTS
for tag in "${TAGS[@]}"; do
echo "Fetching $tag ..." >&2
DIGESTS[$tag]=$(get_index_digest "$tag")
done
# ── Update docker-image-shas.yml ─────────────────────────────────────────────
{
echo "# Docker image SHA lock file."
echo "# Maps image tags to their multi-arch OCI index digests (amd64 + arm64)."
echo "# Regenerate with: .github/scripts/update-docker-shas.sh"
echo ""
echo "${IMAGE}:"
for tag in "${TAGS[@]}"; do
printf " %-20s \"%s\"\n" "${tag}:" "${DIGESTS[$tag]}"
done
} > "$LOCK_FILE"
echo "Updated $LOCK_FILE" >&2

27
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: CI
on:
push:
branches: ['*']
jobs:
test-linux:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php-rel: ['8.2', '8.3', '8.4', '8.5']
ts-state: [ts, nts]
steps:
- uses: actions/checkout@v5
- name: Setup PHP
id: setup-php
uses: shivammathur/setup-php@v2
with:
php-version: '${{ matrix.php-rel }}'
env:
phpts: '${{ matrix.ts-state }}'
- name: build extension
run: phpize && ./configure --enable-rar && make
- name: run tests
run: rm run-tests.php && cp run-tests8.php run-tests.php && make test

View File

@@ -1,123 +0,0 @@
name: Release binaries
on:
push:
tags:
- 'v[0-9]*.[0-9]*.[0-9]*'
permissions:
contents: read
jobs:
check-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check version consistency
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
HEADER_VERSION=$(grep -oP '(?<=#define PHP_RAR_VERSION ")[^"]+' php_rar.h)
PACKAGE_VERSION=$(python3 -c "
import xml.etree.ElementTree as ET
t = ET.parse('package.xml')
ns = {'p': 'http://pear.php.net/dtd/package-2.0'}
print(t.find('p:version/p:release', ns).text)
")
echo "Tag version: $TAG_VERSION"
echo "Header version: $HEADER_VERSION"
echo "Package version: $PACKAGE_VERSION"
if [ "$TAG_VERSION" != "$HEADER_VERSION" ]; then
echo "ERROR: Tag version ($TAG_VERSION) does not match PHP_RAR_VERSION in php_rar.h ($HEADER_VERSION)"
exit 1
fi
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
echo "ERROR: Tag version ($TAG_VERSION) does not match version in package.xml ($PACKAGE_VERSION)"
exit 1
fi
echo "All versions match: $TAG_VERSION"
create-draft-release:
needs: [check-version]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-tags: 'true'
ref: ${{ github.ref }}
- name: Create draft release from tag
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --draft --notes-from-tag
windows-extension-matrix:
needs: [create-draft-release]
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.extension-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- name: Get the extension matrix
id: extension-matrix
uses: php/php-windows-builder/extension-matrix@v1
with:
php-version-list: '8.0, 8.1, 8.2, 8.3, 8.4, 8.5'
windows-build:
needs: [windows-extension-matrix]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.windows-extension-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- name: Build the extension for Windows
uses: php/php-windows-builder/extension@v1
with:
php-version: ${{ matrix.php-version }}
arch: ${{ matrix.arch }}
ts: ${{ matrix.ts }}
args: --enable-rar=shared
test-runner: run-tests-rar.php
windows-release:
needs: [windows-build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Upload artifacts to the release
uses: php/php-windows-builder/release@v1
with:
release: ${{ github.ref_name }}
token: ${{ secrets.GITHUB_TOKEN }}
draft: 'true'
pecl-package:
needs: [create-draft-release]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: pecl
- name: Build PECL package
run: pecl package
- name: Upload PECL package to release
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.ref_name }}" rar-*.tgz

View File

@@ -1,92 +0,0 @@
name: Tests
on:
push:
pull_request:
jobs:
check-dev-version:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v4
- name: Check PHP_RAR_VERSION ends in -dev
run: |
HEADER_VERSION=$(grep -oP '(?<=#define PHP_RAR_VERSION ")[^"]+' php_rar.h)
echo "Header version: $HEADER_VERSION"
if [[ "$HEADER_VERSION" != *-dev ]]; then
echo "ERROR: PHP_RAR_VERSION ($HEADER_VERSION) does not end in -dev on master"
exit 1
fi
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
python3 <<'EOF'
import yaml, json, os
with open('.github/docker-image-shas.yml') as f:
data = yaml.safe_load(f)
image = 'datadog/dd-appsec-php-ci'
includes = []
for tag, sha in data[image].items():
# tag: "php-7.0-debug" or "php-7.0-release-zts"
ver, variant = tag[len('php-'):].split('-', 1)
includes.append({'php': ver, 'variant': variant, 'image_sha': sha})
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write('matrix=' + json.dumps({'include': includes}) + '\n')
EOF
linux:
name: PHP ${{ matrix.php }} (${{ matrix.variant }})
needs: generate-matrix
runs-on: ubuntu-latest
container:
image: datadog/dd-appsec-php-ci@${{ matrix.image_sha }}
options: --user root
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build and test
run: bash .github/scripts/build-and-test.sh
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-php${{ matrix.php }}-${{ matrix.variant }}
path: report.xml
windows:
name: Windows PHP 8.1 TS
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build and test
uses: php/php-windows-builder/extension@v1
with:
php-version: '8.1'
arch: x64
ts: ts
args: --enable-rar=shared
test-runner: ${{ github.workspace }}/run-tests-rar.php
test-workers: 1
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-windows-php8.1-ts
path: '*.xml'

49
.github/workflows/windows.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Publish Windows Releases
on:
release:
types: [created]
push:
branches: ['*']
permissions:
contents: write
jobs:
get-extension-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.extension-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Get the extension matrix
id: extension-matrix
uses: php/php-windows-builder/extension-matrix@v1
build:
needs: get-extension-matrix
runs-on: ${{ matrix.os }}
continue-on-error: false
strategy:
fail-fast: true
matrix: ${{fromJson(needs.get-extension-matrix.outputs.matrix)}}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Build the extension
uses: php/php-windows-builder/extension@v1
with:
php-version: ${{ matrix.php-version }}
arch: ${{ matrix.arch }}
ts: ${{ matrix.ts }}
args: '--enable-rar'
test-runner: 'run-tests8.php'
release:
runs-on: ubuntu-latest
needs: build
if: ${{ github.event_name == 'release' }}
steps:
- name: Upload artifact to the release
uses: php/php-windows-builder/release@v1
with:
release: ${{ github.event.release.tag_name }}
# token: ${{ secrets.GITHUB_TOKEN }}

8
.gitignore vendored
View File

@@ -1,6 +1,5 @@
*.o
*.lo
/run-tests.php
/tests/*.sh
/tests/*.exp
/tests/*.diff
@@ -8,9 +7,11 @@
/tests/*.php
/tests/*.out
/tests/*.mem
/run-tests.php
/modules
/missing
/.deps
*.dep
/.libs
/Makefile
/Makefile.fragments
@@ -47,8 +48,3 @@
/php-rar.creator.user
/compile_commands.json
/.clangd
/report.xml
/.worktrees
*.dep
/configure~
/.cache/

113
Justfile
View File

@@ -1,113 +0,0 @@
# Build and test inside the matching CI Docker image.
# Image SHAs are read from .github/docker-image-shas.yml (multi-arch OCI index digests).
# To refresh SHAs: .github/scripts/update-docker-shas.sh
# ── Images ────────────────────────────────────────────────────────────────────
_base := "datadog/dd-appsec-php-ci@"
_shas := ".github/docker-image-shas.yml"
image_7_0_debug := _base + `grep 'php-7.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_0_release_zts := _base + `grep 'php-7.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_1_debug := _base + `grep 'php-7.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_1_release_zts := _base + `grep 'php-7.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_2_debug := _base + `grep 'php-7.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_2_release_zts := _base + `grep 'php-7.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_3_debug := _base + `grep 'php-7.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_3_release_zts := _base + `grep 'php-7.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_4_debug := _base + `grep 'php-7.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_7_4_release_zts := _base + `grep 'php-7.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_0_debug := _base + `grep 'php-8.0-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_0_release_zts := _base + `grep 'php-8.0-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_1_debug := _base + `grep 'php-8.1-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_1_release_zts := _base + `grep 'php-8.1-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_2_debug := _base + `grep 'php-8.2-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_2_release_zts := _base + `grep 'php-8.2-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_3_debug := _base + `grep 'php-8.3-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_3_release_zts := _base + `grep 'php-8.3-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_4_debug := _base + `grep 'php-8.4-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_4_release_zts := _base + `grep 'php-8.4-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_5_debug := _base + `grep 'php-8.5-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_8_5_release_zts := _base + `grep 'php-8.5-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
_run := "docker run --rm --entrypoint bash -v \"$PWD:/workspace\" -w /workspace --user root"
# ── Default ───────────────────────────────────────────────────────────────────
default:
@just --list
# ── Individual targets ────────────────────────────────────────────────────────
test-7_0-debug:
{{_run}} {{image_7_0_debug}} .github/scripts/build-and-test.sh
test-7_0-release-zts:
{{_run}} {{image_7_0_release_zts}} .github/scripts/build-and-test.sh
test-7_1-debug:
{{_run}} {{image_7_1_debug}} .github/scripts/build-and-test.sh
test-7_1-release-zts:
{{_run}} {{image_7_1_release_zts}} .github/scripts/build-and-test.sh
test-7_2-debug:
{{_run}} {{image_7_2_debug}} .github/scripts/build-and-test.sh
test-7_2-release-zts:
{{_run}} {{image_7_2_release_zts}} .github/scripts/build-and-test.sh
test-7_3-debug:
{{_run}} {{image_7_3_debug}} .github/scripts/build-and-test.sh
test-7_3-release-zts:
{{_run}} {{image_7_3_release_zts}} .github/scripts/build-and-test.sh
test-7_4-debug:
{{_run}} {{image_7_4_debug}} .github/scripts/build-and-test.sh
test-7_4-release-zts:
{{_run}} {{image_7_4_release_zts}} .github/scripts/build-and-test.sh
test-8_0-debug:
{{_run}} {{image_8_0_debug}} .github/scripts/build-and-test.sh
test-8_0-release-zts:
{{_run}} {{image_8_0_release_zts}} .github/scripts/build-and-test.sh
test-8_1-debug:
{{_run}} {{image_8_1_debug}} .github/scripts/build-and-test.sh
test-8_1-release-zts:
{{_run}} {{image_8_1_release_zts}} .github/scripts/build-and-test.sh
test-8_2-debug:
{{_run}} {{image_8_2_debug}} .github/scripts/build-and-test.sh
test-8_2-release-zts:
{{_run}} {{image_8_2_release_zts}} .github/scripts/build-and-test.sh
test-8_3-debug:
{{_run}} {{image_8_3_debug}} .github/scripts/build-and-test.sh
test-8_3-release-zts:
{{_run}} {{image_8_3_release_zts}} .github/scripts/build-and-test.sh
test-8_4-debug:
{{_run}} {{image_8_4_debug}} .github/scripts/build-and-test.sh
test-8_4-release-zts:
{{_run}} {{image_8_4_release_zts}} .github/scripts/build-and-test.sh
test-8_5-debug:
{{_run}} {{image_8_5_debug}} .github/scripts/build-and-test.sh
test-8_5-release-zts:
{{_run}} {{image_8_5_release_zts}} .github/scripts/build-and-test.sh
# ── Per-version aggregates (sequential to avoid workspace conflicts) ───────────
test-7_0: test-7_0-debug test-7_0-release-zts
test-7_1: test-7_1-debug test-7_1-release-zts
test-7_2: test-7_2-debug test-7_2-release-zts
test-7_3: test-7_3-debug test-7_3-release-zts
test-7_4: test-7_4-debug test-7_4-release-zts
test-8_0: test-8_0-debug test-8_0-release-zts
test-8_1: test-8_1-debug test-8_1-release-zts
test-8_2: test-8_2-debug test-8_2-release-zts
test-8_3: test-8_3-debug test-8_3-release-zts
test-8_4: test-8_4-debug test-8_4-release-zts
test-8_5: test-8_5-debug test-8_5-release-zts
# ── All Linux targets ─────────────────────────────────────────────────────────
test-linux: test-7_0 test-7_1 test-7_2 test-7_3 test-7_4 test-8_0 test-8_1 test-8_2 test-8_3 test-8_4 test-8_5

View File

@@ -1,5 +1,6 @@
.PHONY: replace-run-tests
replace-run-tests:
cp run-tests-rar.php run-tests.php
@if ! grep -q 'Minimum required PHP version: 5\.3\.0' run-tests.php; then \
cp run-tests8.php run-tests.php; \
fi
test: replace-run-tests

View File

@@ -10,22 +10,33 @@ unrar/LICENSE.txt for details.
Some modifications have been applied to the UnRAR library, mainly to allow
streaming extraction of files without using threads.
## Installation
| Version | Status |
|---------|------------------------------|
| master | unmaintened :x: |
| v4.x | maintened :white_check_mark: |
### With PECL
Maintained PHP Versions compatibility:
```sh
pecl install rar
| PHP Version | Status |
|-------------|------------------------|
| 5.x | no :x: |
| 7.x | no :x: |
| 8.0 | yes :white_check_mark: |
| 8.1 | yes :white_check_mark: |
| 8.2 | yes :white_check_mark: |
| 8.3 | yes :white_check_mark: |
| 8.4 | yes :white_check_mark: |
| 8.5 | yes :white_check_mark: |
Installation system support:
| Platform | Status |
|----------|------------------------|
| PECL | no :x: |
| PIE | yes :white_check_mark: |
To install the extension, use PIE (PHP Installer Extension) with a command like:
```bash
pie install php-win-ext/rar
```
Then add `extension=rar` to your `php.ini`.
### With PIE
[PIE](https://github.com/php/pie) is the modern replacement for PECL, available from PHP 8.1+.
```sh
pie install rar
```
PIE automatically adds the extension to your `php.ini`.

View File

@@ -1,31 +1,21 @@
{
"name": "cataphract/rar",
"name": "php-win-ext/rar",
"type": "php-ext",
"description": "PHP extension for reading RAR archives using bundled unRAR library",
"license": "PHP-3.01",
"license": [
"PHP-3.01"
],
"authors": [
{
"name": "Gustavo Lopes",
"email": "cataphract@php.net",
"role": "lead"
"email": "cataphract@php.net"
},
{
"name": "Antony Dovgal",
"email": "tony@daylessday.org",
"role": "developer"
"email": "tony@daylessday.org"
}
],
"require": {
"php": "^7.0 || ^8.0"
"php": ">= 8.0.0"
},
"replace": {
"ext-rar": "*"
},
"support": {
"source": "https://github.com/cataphract/php-rar"
},
"php-ext": {
"extension-name": "rar",
"download-url-method": ["pre-packaged-binary", "composer-default"]
}
}
"description": "rar extension"
}

View File

@@ -29,8 +29,7 @@ unrar_sources="unrar/sha256.cpp unrar/qopen.cpp \
unrar/arcread.cpp unrar/filefn.cpp \
unrar/global.cpp unrar/list.cpp \
unrar/encname.cpp unrar/file.cpp \
unrar/secpassword.cpp unrar/options.cpp \
unrar/largepage.cpp"
unrar/secpassword.cpp unrar/options.cpp"
AC_LANG_PUSH([C++])
@@ -69,6 +68,6 @@ if test "$PHP_RAR" != "no"; then
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", RAR_SHARED_LIBADD)
PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -Wall -fPIC -fvisibility=hidden -I@ext_srcdir@/unrar)
PHP_NEW_EXTENSION(rar, rar.c rar_error.c rararch.c rarentry.c rar_stream.c rar_navigation.c rar_time.c $unrar_sources, $ext_shared,,-DRARDLL -DSILENT -Wno-write-strings -Wall -fvisibility=hidden -I@ext_srcdir@/unrar)
PHP_ADD_BUILD_DIR($ext_builddir/unrar)
fi

View File

@@ -18,7 +18,7 @@ if (PHP_RAR != "no") {
crc.cpp rijndael.cpp crypt.cpp \
rawread.cpp \
rs.cpp smallfn.cpp \
isnt.cpp consio.cpp \
isnt.cpp rar.cpp consio.cpp \
scantree.cpp archive.cpp strfn.cpp \
strlist.cpp \
getbits.cpp hash.cpp \
@@ -31,8 +31,7 @@ if (PHP_RAR != "no") {
arcread.cpp filefn.cpp \
global.cpp list.cpp \
encname.cpp file.cpp \
secpassword.cpp options.cpp \
largepage.cpp motw.cpp", "rar");
secpassword.cpp options.cpp", "rar", "unrar_obj");
AC_DEFINE("HAVE_RAR", 1, "Rar support");
}

View File

@@ -23,11 +23,11 @@ http://pear.php.net/dtd/package-2.0.xsd">
<active>no</active>
</developer>
<date>2026-03-08</date>
<date>2020-12-06</date>
<time>20:00:00</time>
<version>
<release>4.3.0</release>
<api>4.0.0</api>
<release>4.2.0</release>
<api>4.2.0</api>
</version>
<stability>
@@ -36,10 +36,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
</stability>
<license uri="http://www.php.net/license">PHP License</license>
<notes>- Add PHP 8.1, 8.2, 8.3, 8.4, 8.5 support.
- Drop PHP 5 support; minimum PHP version is now 7.0.
- Update bundled unrar to 7.2.4.
- Fix Windows bug
<notes>- Support PHP 8.
- Merge unrar 6.0.2.
- RarArchive implements IteratorAggregate (PHP 8 only).
</notes>
<contents>
<dir name="/">
@@ -112,6 +111,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file role="test" name="065.phpt"/>
<file role="test" name="066.phpt"/>
<file role="test" name="067.phpt"/>
<file role="test" name="068.phpt"/>
<file role="test" name="069.phpt"/>
<file role="test" name="070.phpt"/>
<file role="test" name="071.phpt"/>
<file role="test" name="072.phpt"/>
@@ -147,6 +148,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file role="test" name="102.phpt"/>
<file role="test" name="103.phpt"/>
<file role="test" name="104.phpt"/>
<file role="test" name="105.phpt"/>
<file role="test" name="106.phpt"/>
<file role="test" name="107.phpt"/>
<file role="test" name="108.phpt"/>
@@ -191,6 +193,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="archive.cpp" role="src" />
<file name="archive.hpp" role="src" />
<file name="arcread.cpp" role="src" />
<file name="array.hpp" role="src" />
<file name="blake2s.cpp" role="src" />
<file name="blake2s.hpp" role="src" />
<file name="blake2s_sse.cpp" role="src" />
@@ -245,8 +248,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="headers5.hpp" role="src" />
<file name="isnt.cpp" role="src" />
<file name="isnt.hpp" role="src" />
<file name="largepage.cpp" role="src" />
<file name="largepage.hpp" role="src" />
<file name="LICENSE.txt" role="doc" />
<file name="list.cpp" role="src" />
<file name="list.hpp" role="src" />
@@ -257,8 +258,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="match.hpp" role="src" />
<file name="model.cpp" role="src" />
<file name="model.hpp" role="src" />
<file name="motw.cpp" role="src" />
<file name="motw.hpp" role="src" />
<file name="options.cpp" role="src" />
<file name="options.hpp" role="src" />
<file name="os.hpp" role="src" />
@@ -308,6 +307,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="rijndael.hpp" role="src" />
<file name="rs.cpp" role="src" />
<file name="rs.hpp" role="src" />
<file name="savepos.hpp" role="src" />
<file name="scantree.cpp" role="src" />
<file name="scantree.hpp" role="src" />
<file name="secpassword.cpp" role="src" />
@@ -340,6 +340,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="win32acl.cpp" role="src" />
<file name="win32stm.cpp" role="src" />
</dir> <!-- /unrar -->
<file name="config.m4" role="src" />
<file name="config.m4" role="src" />
<file name="config.w32" role="src" />
<file name="CREDITS" role="doc" />
@@ -355,7 +356,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file name="rarentry.c" role="src" />
<file name="rar_error.c" role="src" />
<file name="rar_time.c" role="src" />
<file name="run-tests-rar.php" role="test" />
<file name="run-tests8.php" role="test" />
<file role="src" name="rar_navigation.c"/>
</dir> <!-- / -->
</contents>
@@ -363,7 +364,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<dependencies>
<required>
<php>
<min>7.0.0</min>
<min>5.3.0</min>
</php>
<pearinstaller>
<min>1.4.0</min>
@@ -375,44 +376,6 @@ http://pear.php.net/dtd/package-2.0.xsd">
<extsrcrelease />
<changelog>
<release>
<version>
<release>4.3.0</release>
<api>4.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<date>2026-03-08</date>
<notes>Changes in this version:
- Add PHP 8.1, 8.2, 8.3 support.
- Drop PHP 5 support; minimum PHP version is now 7.0.
- Update bundled unrar to 7.2.4.
- Fix segfault caused by uninitialized RARHeaderDataEx.
- Fix dll.cpp: don't propagate non-fatal ErrHandler errors as failures.
- Fix RAR5 chunk extraction spurious warning on Windows with multi-core CPUs.
- Migrate CI from Azure Pipelines/Appveyor to GitHub Actions.
</notes>
</release>
<release>
<version>
<release>4.2.0</release>
<api>4.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<date>2020-12-06</date>
<notes>Changes in this version:
- Support PHP 8.
- Merge unrar 6.0.2.
- RarArchive implements IteratorAggregate (PHP 8 only).
</notes>
</release>
<release>
<version>
<release>4.1.0</release>

View File

@@ -15,6 +15,7 @@ typedef zend_object handler_this_t;
typedef zval handler_this_t;
#endif
#if PHP_MAJOR_VERSION >= 7
typedef zend_object* rar_obj_ref;
#define rar_zval_add_ref(ppzv) zval_add_ref(*ppzv)
@@ -52,3 +53,20 @@ typedef size_t zpp_s_size_t;
#define INIT_ZVAL(zv) ZVAL_UNDEF(&zv)
#define ZEND_ACC_FINAL_CLASS ZEND_ACC_FINAL
#else /* PHP 5.x */
typedef zend_object_handle rar_obj_ref;
#define rar_zval_add_ref zval_add_ref
#define ZVAL_ALLOC_DUP(dst, src) \
do { \
zval *z_src = src; \
dst = z_src; \
zval_add_ref(&dst); \
SEPARATE_ZVAL(&dst); \
} while (0)
#define RAR_ZVAL_STRING ZVAL_STRING
#define RAR_RETURN_STRINGL(s, l, duplicate) RETURN_STRINGL(s, l, duplicate)
typedef int zpp_s_size_t;
#define zend_hash_str_del zend_hash_del
#endif

View File

@@ -51,7 +51,7 @@
extern zend_module_entry rar_module_entry;
#define phpext_rar_ptr &rar_module_entry
#define PHP_RAR_VERSION "4.3.1-dev"
#define PHP_RAR_VERSION "4.2.1"
#ifdef PHP_WIN32
#define PHP_RAR_API __declspec(dllexport)
@@ -77,7 +77,6 @@ extern zend_module_entry rar_module_entry;
#include "unrar/dll.hpp"
#include "unrar/version.hpp"
/* These are in unrar/headers.hpp, but that header depends on several other */
/* clang-format off */
enum HOST_SYSTEM {
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
HOST_BEOS=5,HOST_MAX
@@ -86,12 +85,10 @@ enum FILE_SYSTEM_REDIRECT {
FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
FSREDIR_HARDLINK, FSREDIR_FILECOPY
};
/* clang-format on */
/* maximum comment size if 64KB */
#define RAR_MAX_COMMENT_SIZE 65536
/* clang-format off */
typedef struct _rar_cb_user_data {
char *password; /* can be NULL */
zval *callable; /* can be NULL */
@@ -108,19 +105,13 @@ typedef struct rar {
rar_cb_user_data cb_userdata;
int allow_broken;
} rar_file_t;
/* clang-format on */
/* Misc */
# if defined(__GNUC__) || defined(__clang__)
# define ARR_SIZE(arr) \
(sizeof(arr) / sizeof((arr)[0]) + \
0 * sizeof(char[1 - 2 * __builtin_types_compatible_p( \
__typeof__(arr), __typeof__(&(arr)[0]))]))
# else
# define ARR_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
# endif
#if defined(ZTS) && PHP_MAJOR_VERSION < 7
# define RAR_TSRMLS_TC , void ***
#else
# define RAR_TSRMLS_TC
#endif
#define RAR_RETNULL_ON_ARGS() \
if (zend_parse_parameters_none() == FAILURE) { \
@@ -167,6 +158,33 @@ ZEND_EXTERN_MODULE_GLOBALS(rar);
# define RAR_G(v) (rar_globals.v)
#endif
/* PHP 5.2 compatibility */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3
#define zend_parse_parameters_none() \
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "")
#define Z_DELREF_P ZVAL_DELREF
# define STREAM_ASSUME_REALPATH 0
# define ALLOC_PERMANENT_ZVAL(z) \
(z) = (zval*) malloc(sizeof(zval));
# define OPENBASEDIR_CHECKPATH(filename) \
(PG(safe_mode) && \
(!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) \
|| php_check_open_basedir(filename TSRMLS_CC)
# undef ZEND_BEGIN_ARG_INFO_EX
# define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \
static const zend_arg_info name[] = { \
{ NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },
#endif
/* Other compatibility quirks */
/* PHP 5.3 doesn't have ZVAL_COPY_VALUE */
#if !defined(ZEND_COPY_VALUE) && PHP_MAJOR_VERSION == 5
#define ZVAL_COPY_VALUE(z, v) \
do { \
(z)->value = (v)->value; \
Z_TYPE_P(z) = Z_TYPE_P(v); \
} while (0)
#endif
#if !defined(HAVE_STRNLEN) || !HAVE_STRNLEN
size_t _rar_strnlen(const char *s, size_t maxlen);
@@ -230,7 +248,7 @@ typedef struct _rar_find_output {
int found;
size_t position;
struct RARHeaderDataEx * header;
zend_ulong packed_size;
unsigned long packed_size;
int eof;
} rar_find_output;
#define RAR_SEARCH_INDEX 0x01U
@@ -279,7 +297,7 @@ extern zend_class_entry *rar_class_entry_ptr;
void minit_rarentry(TSRMLS_D);
void _rar_entry_to_zval(zval *parent,
struct RARHeaderDataEx *entry,
zend_ulong packed_size,
unsigned long packed_size,
size_t index,
zval *entry_object TSRMLS_DC);

View File

@@ -1,189 +0,0 @@
# PHP 8.18.5 Upgrade Procedure for php-rar
This document describes the step-by-step procedure to extend php-rar support
from PHP 8.0 to PHP 8.5. Each minor version is handled independently: CI is
wired up, the extension is compiled and tested inside the matching Docker image,
code changes are applied to fix any failures, and only then is the next version
tackled.
---
## Overview of files touched per version
| File | Change |
|---|---|
| `.github/docker-image-shas.yml` | Add new tag → SHA entries |
| `.github/scripts/update-docker-shas.sh` | Add new tags to the `TAGS` array |
| `Justfile` | Add image variables and `test-X_Y-*` targets |
| `*.c` / `*.h` | C source changes for API compatibility |
| `.github/workflows/tests.yml` | Windows job — update `php-version` (once per bump) |
The Linux CI matrix is generated automatically from `docker-image-shas.yml`, so
no manual edit to `tests.yml` is needed for Linux jobs.
---
## Repeatable procedure for each version
Follow these numbered steps for **each** minor version in order. Example:
8.0 → 8.1 → 8.2 → 8.3 → 8.4 → 8.5.
### Step 1 — Read the upgrade guides in php-src
Clone or browse php-src on the target branch, e.g. `PHP-8.1`:
```
https://github.com/php/php-src/blob/PHP-8.X/UPGRADING
https://github.com/php/php-src/blob/PHP-8.X/UPGRADING.INTERNALS
```
Focus on sections relevant to C extensions:
- Removed or renamed macros / functions
- Changed return types (`int``zend_result`)
- Changed struct member types
- New mandatory includes
- Any other backwards-incompatible changes
The per-version notes below summarise the items relevant to php-rar.
### Step 2 — Add the Docker image SHA
Fetch the OCI index digest from Docker Hub for the two new tags:
```bash
# Quick one-liner — prints the index digest for a given tag
curl -fsSL "https://hub.docker.com/v2/repositories/datadog/dd-appsec-php-ci/tags/php-X.Y-debug" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
```
Or regenerate everything at once with the provided script after adding the new
tags to it (see Step 3):
```bash
.github/scripts/update-docker-shas.sh
```
Append the two lines to `.github/docker-image-shas.yml`:
```yaml
php-X.Y-debug: "sha256:<INDEX-DIGEST>"
php-X.Y-release-zts: "sha256:<INDEX-DIGEST>"
```
Also add both tags to the `TAGS` array in `.github/scripts/update-docker-shas.sh`:
```bash
TAGS=(
...existing tags...
php-X.Y-debug php-X.Y-release-zts
)
```
### Step 3 — Add Justfile targets
Add image variables and `test-X_Y-*` targets following the existing pattern:
```just
image_X_Y_debug := _base + `grep 'php-X.Y-debug:' .github/docker-image-shas.yml | cut -d'"' -f2`
image_X_Y_release_zts := _base + `grep 'php-X.Y-release-zts:' .github/docker-image-shas.yml | cut -d'"' -f2`
test-X_Y-debug:
{{_run}} {{image_X_Y_debug}} .github/scripts/build-and-test.sh
test-X_Y-release-zts:
{{_run}} {{image_X_Y_release_zts}} .github/scripts/build-and-test.sh
test-X_Y: test-X_Y-debug test-X_Y-release-zts
```
Add `test-X_Y` to the `test-linux` aggregate at the bottom.
### Step 4 — Compile and test
Run both variants locally before pushing:
```bash
just test-X_Y-debug
just test-X_Y-release-zts
```
Or both together:
```bash
just test-X_Y
```
Examine the output for compiler warnings, errors, and test failures.
### Step 5 — Apply C source changes
Based on the compilation output and the per-version notes below, make the
minimum necessary changes to `.c`/`.h` files. Guard every change with `#if
PHP_VERSION_ID >= XXYY00` so that older PHP versions continue to work.
### Step 6 — Re-run tests until green
Repeat Step 4 after each change. When both `debug` and `release-zts` pass,
commit.
### Step 7 — Push and verify CI
Push the branch. The `linux` CI job matrix is auto-built from
`docker-image-shas.yml` — the new versions appear automatically. Verify the
GitHub Actions run is green for all new jobs.
### Step 8 — Update Windows CI (optional, once per bump)
The Windows job in `.github/workflows/tests.yml` pins a specific PHP version.
Update it when the Linux jobs for the matching version are confirmed green:
```yaml
- name: Build and test
uses: php/php-windows-builder/extension@v1
with:
php-version: 'X.Y' # ← change here
```
Also update the `name:` and artifact `name:` strings in the same Windows job
block.
---
## Current Docker image SHAs (as of 2026-03-01)
These are the OCI index digests (multi-arch: amd64 + arm64) to use in
`docker-image-shas.yml`.
```yaml
php-8.1-debug: "sha256:1a1e5b44cf043e59768c65fd7c94aaefdacde5fa96d83102d35db11ad86f24c6"
php-8.1-release-zts: "sha256:5b8a269b4228d9191420059daef820b660110be0aca6776557924172fd1ff0c8"
php-8.2-debug: "sha256:52ad14560672fc8c5130f5758bbee3fa401bc1d35b412f4a230c6258143291a5"
php-8.2-release-zts: "sha256:cb143d915b394f16a2d78018765705460f3d1b788fdd2a90ef50fad5f8f5918c"
php-8.3-debug: "sha256:bb6df08160126374d3d9247428928aa19a9c2b2429c98356650199b85ae20212"
php-8.3-release-zts: "sha256:e58e25a017f75df82691d408b8cb70453875ff36718e295ee8c6653a0f117331"
php-8.4-debug: "sha256:15045688f6986f4625b1507a7f4be6104e7bbb88caf877f1611463b929f2bca2"
php-8.4-release-zts: "sha256:8e0ac25a3306b4b9f692c593b8a509cc789c2e001ce52682928065a92c880136"
php-8.5-debug: "sha256:bd0170331b34fb469e29d00b19b20fb88b726160f76df274a1bdc3a27ac18d30"
php-8.5-release-zts: "sha256:e071b2095da55bd24686209422f43a01c65acfc6021f04156d9fb43fd3d4d426"
```
Refresh at any time with `.github/scripts/update-docker-shas.sh` after adding
the new tags.
---
## Summary checklist
For each version X.Y in order (8.1, 8.2, 8.3, 8.4, 8.5):
- [ ] Read `PHP-X.Y/UPGRADING.INTERNALS` on GitHub
- [ ] Add two SHA entries to `.github/docker-image-shas.yml`
- [ ] Add both tags to `TAGS` array in `.github/scripts/update-docker-shas.sh`
- [ ] Add `image_X_Y_*` variables and `test-X_Y-*` targets to `Justfile`
- [ ] Add `test-X_Y` to `test-linux` aggregate in `Justfile`
- [ ] Run `just test-X_Y` and fix all compilation errors
- [ ] Run `just test-X_Y` again; confirm all tests pass
- [ ] Commit infrastructure + code changes together
- [ ] Push; confirm GitHub Actions CI is green for the new matrix entries
- [ ] (Optional) Update Windows `php-version` in `.github/workflows/tests.yml` to X.Y
<!-- vim: set tw=80: -->

115
rar.c
View File

@@ -28,12 +28,14 @@
/* $Id$ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#include "config.h"
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#ifdef __cplusplus
extern "C" {
#endif
#define _GNU_SOURCE
#include <string.h>
#ifdef PHP_WIN32
@@ -48,7 +50,7 @@
#include <ext/standard/info.h>
#include <ext/spl/spl_exceptions.h>
#include "unrar/rardefs.hpp"
#if HAVE_RAR
#include "php_rar.h"
@@ -159,8 +161,12 @@ void _rar_destroy_userdata(rar_cb_user_data *udata) /* {{{ */
}
if (udata->callable != NULL) {
#if PHP_MAJOR_VERSION < 7
zval_ptr_dtor(&udata->callable);
#else
zval_ptr_dtor(udata->callable);
efree(udata->callable);
#endif
}
udata->password = NULL;
@@ -234,16 +240,13 @@ int _rar_find_file_w(struct RAROpenArchiveDataEx *open_data, /* IN */
while ((result = RARReadHeaderEx(*arc_handle, used_header_data)) == 0) {
#if WCHAR_MAX > 0xffff
_rar_fix_wide(used_header_data->FileNameW,
ARR_SIZE(used_header_data->FileNameW));
_rar_fix_wide(used_header_data->FileNameW, NM);
#endif
if (wcsncmp(used_header_data->FileNameW, file_name,
ARR_SIZE(used_header_data->FileNameW)) == 0) {
if (wcsncmp(used_header_data->FileNameW, file_name, NM) == 0) {
*found = TRUE;
goto cleanup;
}
else {
} else {
process_result = RARProcessFile(*arc_handle, RAR_SKIP, NULL, NULL);
}
if (process_result != 0) {
@@ -380,7 +383,6 @@ int CALLBACK _rar_unrar_callback(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2
return ret;
}
}
// TODO: maybe support UCM_NEEDPASSWORDW and UCM_CHANGEVOLUMEW
return 0;
}
@@ -423,7 +425,7 @@ PHP_FUNCTION(rar_wrapper_cache_stats) /* {{{ */
static void _rar_fix_wide(wchar_t *str, size_t max_size) /* {{{ */
{
wchar_t *write,
*read,
*read,
*max_fin;
max_fin = str + max_size;
for (write = str, read = str; *read != L'\0' && read != max_fin; read++) {
@@ -440,37 +442,61 @@ static void _rar_fix_wide(wchar_t *str, size_t max_size) /* {{{ */
* because, in case we're using exceptions, we want to let an exception with
* error code ERAR_EOPEN to be thrown.
*/
static int _rar_unrar_volume_user_callback(char* dst_buffer, // MAXPATHSIZE
static int _rar_unrar_volume_user_callback(char* dst_buffer,
zend_fcall_info *fci,
zend_fcall_info_cache *cache
TSRMLS_DC) /* {{{ */
{
#if PHP_MAJOR_VERSION < 7
zval *failed_vol,
*retval_ptr = NULL,
**params;
#else
zval failed_vol,
retval,
*params,
*const retval_ptr = &retval;
#endif
int ret = -1;
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(failed_vol);
RAR_ZVAL_STRING(failed_vol, dst_buffer, 1);
params = &failed_vol;
fci->retval_ptr_ptr = &retval_ptr;
fci->params = &params;
#else
ZVAL_STRING(&failed_vol, dst_buffer);
ZVAL_NULL(&retval);
params = &failed_vol;
fci->retval = &retval;
fci->params = params;
#endif
fci->param_count = 1;
#if PHP_MAJOR_VERSION < 7
if (zend_call_function(fci, cache TSRMLS_CC) != SUCCESS ||
fci->retval_ptr_ptr == NULL ||
*fci->retval_ptr_ptr == NULL) {
#else
if (zend_call_function(fci, cache TSRMLS_CC) != SUCCESS || EG(exception)) {
#endif
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Failure to call volume find callback");
goto cleanup;
}
#if PHP_MAJOR_VERSION < 7
assert(*fci->retval_ptr_ptr == retval_ptr);
#else
assert(fci->retval == &retval);
#endif
if (Z_TYPE_P(retval_ptr) == IS_NULL) {
/* let return -1 */
}
else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
char *filename = Z_STRVAL_P(retval_ptr);
char resolved_path[MAXPATHSIZE];
char resolved_path[MAXPATHLEN];
size_t resolved_len;
if (OPENBASEDIR_CHECKPATH(filename)) {
@@ -482,15 +508,17 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, // MAXPATHSIZE
goto cleanup;
}
resolved_len = _rar_strnlen(resolved_path, MAXPATHSIZE);
if (resolved_len > MAXPATHSIZE - 1) {
resolved_len = _rar_strnlen(resolved_path, MAXPATHLEN);
/* dst_buffer size is NM; first condition won't happen short of a bug
* in expand_filepath */
if (resolved_len == MAXPATHLEN || resolved_len > NM - 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Resolved path is too big for the unRAR library");
goto cleanup;
}
strncpy(dst_buffer, resolved_path, MAXPATHSIZE);
dst_buffer[MAXPATHSIZE - 1] = '\0';
strncpy(dst_buffer, resolved_path, NM);
dst_buffer[NM - 1] = '\0';
ret = 1; /* try this new filename */
}
else {
@@ -501,8 +529,15 @@ static int _rar_unrar_volume_user_callback(char* dst_buffer, // MAXPATHSIZE
}
cleanup:
#if PHP_MAJOR_VERSION < 7
zval_ptr_dtor(&failed_vol);
if (retval_ptr != NULL) {
zval_ptr_dtor(&retval_ptr);
}
#else
zval_ptr_dtor(&failed_vol);
zval_ptr_dtor(&retval);
#endif
return ret;
}
/* }}} */
@@ -518,6 +553,17 @@ static int _rar_make_userdata_fcall(zval *callable,
*cache = empty_fcall_info_cache;
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2
if (zend_fcall_info_init(callable, fci, cache TSRMLS_CC) != SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"The RAR file was not opened in rar_open/RarArchive::open with a "
"valid callback.", error);
return FAILURE;
}
else {
return SUCCESS;
}
#else
if (zend_fcall_info_init(callable, IS_CALLABLE_STRICT, fci, cache, NULL,
&error TSRMLS_CC) == SUCCESS) {
if (error) {
@@ -537,6 +583,7 @@ static int _rar_make_userdata_fcall(zval *callable,
}
return FAILURE;
}
#endif
}
/* }}} */
@@ -587,7 +634,6 @@ ZEND_END_ARG_INFO()
/* {{{ rar_functions[]
*
*/
/* clang-format off */
static zend_function_entry rar_functions[] = {
PHP_FE(rar_open, arginfo_rar_open)
PHP_FE(rar_list, arginfo_rar_void_archmeth)
@@ -600,13 +646,16 @@ static zend_function_entry rar_functions[] = {
PHP_FE(rar_wrapper_cache_stats, arginfo_rar_wrapper_cache_stats)
{NULL, NULL, NULL}
};
/* clang-format on */
/* }}} */
/* {{{ Globals' related activities */
ZEND_DECLARE_MODULE_GLOBALS(rar);
#if PHP_MAJOR_VERSION < 7
static int _rar_array_apply_remove_first(void *pDest TSRMLS_DC)
#else
static int _rar_array_apply_remove_first(zval *pDest TSRMLS_DC)
#endif
{
return (ZEND_HASH_APPLY_STOP | ZEND_HASH_APPLY_REMOVE);
}
@@ -624,7 +673,13 @@ static void _rar_contents_cache_put(const char *key,
assert(zend_hash_num_elements(cc->data) == cur_size - 1);
}
rar_zval_add_ref(&zv);
#if PHP_MAJOR_VERSION < 7
assert(Z_REFCOUNT_P(zv) > 1);
SEPARATE_ZVAL(&zv); /* ensure we store a heap allocated copy */
zend_hash_update(cc->data, key, key_len, &zv, sizeof(zv), NULL);
#else
zend_hash_str_update(cc->data, key, key_len, zv);
#endif
}
static zval *_rar_contents_cache_get(const char *key,
@@ -633,7 +688,15 @@ static zval *_rar_contents_cache_get(const char *key,
{
rar_contents_cache *cc = &RAR_G(contents_cache);
zval *element = NULL;
#if PHP_MAJOR_VERSION < 7
zval **element_p = NULL;
zend_hash_find(cc->data, key, key_len, (void **) &element_p);
if (element_p) {
element = *element_p;
}
#else
element = zend_hash_str_find(cc->data, key, key_len);
#endif
if (element != NULL) {
cc->hits++;
@@ -692,14 +755,16 @@ ZEND_MODULE_STARTUP_D(rar)
php_register_url_stream_wrapper("rar", &php_stream_rar_wrapper TSRMLS_CC);
/* clang-format off */
REGISTER_LONG_CONSTANT("RAR_HOST_MSDOS", HOST_MSDOS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RAR_HOST_OS2", HOST_OS2, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RAR_HOST_WIN32", HOST_WIN32, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RAR_HOST_UNIX", HOST_UNIX, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RAR_HOST_MACOS", HOST_MACOS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("RAR_HOST_BEOS", HOST_BEOS, CONST_CS | CONST_PERSISTENT);
/* clang-format on */
/* PHP < 5.3 doesn't have the PHP_MAXPATHLEN constant */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3
REGISTER_LONG_CONSTANT("RAR_MAXPATHLEN", MAXPATHLEN, CONST_CS | CONST_PERSISTENT);
#endif
return SUCCESS;
}
/* }}} */
@@ -764,6 +829,12 @@ zend_module_entry rar_module_entry = {
};
/* }}} */
#endif /* HAVE_RAR */
#ifdef __cplusplus
}
#endif
/*
* Local variables:
* tab-width: 4

View File

@@ -25,6 +25,10 @@
+----------------------------------------------------------------------+
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <php.h>
#include <zend_exceptions.h>
#include "php_rar.h"
@@ -66,7 +70,11 @@ void _rar_handle_ext_error(const char *format TSRMLS_DC, ...) /* {{{ */
va_list arg;
char *message;
#if defined(ZTS) && PHP_MAJOR_VERSION < 7
va_start(arg, TSRMLS_C);
#else
va_start(arg, format);
#endif
vspprintf(&message, 0, format, arg);
va_end(arg);
@@ -83,8 +91,13 @@ int _rar_using_exceptions(TSRMLS_D)
zval *pval;
pval = zend_read_static_property(rarexception_ce_ptr, "usingExceptions",
sizeof("usingExceptions") -1, (zend_bool) 1 TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
assert(Z_TYPE_P(pval) == IS_BOOL);
return Z_BVAL_P(pval);
#else
assert(Z_TYPE_P(pval) == IS_TRUE || Z_TYPE_P(pval) == IS_FALSE);
return Z_TYPE_P(pval) == IS_TRUE;
#endif
}
/* returns a string or NULL if not an error */
@@ -173,22 +186,39 @@ PHP_METHOD(rarexception, setUsingExceptions)
Return whether exceptions are being used */
PHP_METHOD(rarexception, isUsingExceptions)
{
#if PHP_MAJOR_VERSION < 7
zval **pval;
#else
zval *pval;
#endif
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE ) {
return;
}
/* or zend_read_static_property, which calls zend_std_get... after chg scope */
#if PHP_VERSION_ID < 50399
pval = zend_std_get_static_property(rarexception_ce_ptr, "usingExceptions",
sizeof("usingExceptions") -1, (zend_bool) 0 TSRMLS_CC);
#elif PHP_MAJOR_VERSION < 7
pval = zend_std_get_static_property(rarexception_ce_ptr, "usingExceptions",
sizeof("usingExceptions") -1, (zend_bool) 0, NULL TSRMLS_CC);
#else
zend_string *prop_name =
zend_string_init("usingExceptions", sizeof("usingExceptions") - 1, 0);
pval = zend_std_get_static_property(rarexception_ce_ptr, prop_name,
(zend_bool) 0);
zend_string_release(prop_name);
#endif
/* property always exists */
assert(pval != NULL);
#if PHP_MAJOR_VERSION < 7
assert(Z_TYPE_PP(pval) == IS_BOOL);
RETURN_ZVAL(*pval, 0, 0);
#else
assert(Z_TYPE_P(pval) == IS_TRUE || Z_TYPE_P(pval) == IS_FALSE);
RETURN_ZVAL(pval, 0, 0);
#endif
}
/* }}} */
@@ -201,24 +231,31 @@ ZEND_BEGIN_ARG_INFO(arginfo_rarexception_void, 0)
ZEND_END_ARG_INFO()
/* }}} */
/* clang-format off */
static zend_function_entry php_rarexception_class_functions[] = {
PHP_ME(rarexception, setUsingExceptions, arginfo_rarexception_sue, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(rarexception, isUsingExceptions, arginfo_rarexception_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
{NULL, NULL, NULL}
};
/* clang-format on */
void minit_rarerror(TSRMLS_D) /* {{{ */
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "RarException", php_rarexception_class_functions);
/* zend_exception_get_default() was removed in PHP 8.5; use the global directly */
rarexception_ce_ptr = zend_register_internal_class_ex(&ce, zend_ce_exception);
#if PHP_MAJOR_VERSION < 7
rarexception_ce_ptr = zend_register_internal_class_ex(&ce,
zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
#else
rarexception_ce_ptr = zend_register_internal_class_ex(&ce,
zend_ce_exception);
#endif
rarexception_ce_ptr->ce_flags |= ZEND_ACC_FINAL;
zend_declare_property_bool(rarexception_ce_ptr, "usingExceptions",
sizeof("usingExceptions") -1, 0L /* FALSE */,
ZEND_ACC_STATIC TSRMLS_CC);
}
/* }}} */
#ifdef __cplusplus
}
#endif

View File

@@ -25,16 +25,21 @@
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
# include "config.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <php.h>
#include <wchar.h>
#include "php_rar.h"
#if HAVE_RAR
/* {{{ Structure definitions */
/* clang-format off */
typedef struct _rar_find_state {
rar_find_output out;
rar_file_t *rar;
@@ -44,7 +49,7 @@ typedef struct _rar_find_state {
struct _rar_unique_entry {
size_t id; /* position in the entries_array */
struct RARHeaderDataEx entry; /* last entry */
zend_ulong packed_size;
unsigned long packed_size;
int depth; /* number of directory separators */
size_t name_wlen; /* excluding L'\0' terminator */
};
@@ -60,7 +65,6 @@ struct _rar_entries {
struct _rar_unique_entry *last_accessed;
int list_result; /* tell whether the archive's broken */
};
/* clang-format on */
/* }}} */
@@ -69,7 +73,9 @@ static void _rar_nav_get_depth_and_length(wchar_t *filenamew, const size_t file_
int *depth_out, size_t *wlen_out TSRMLS_DC);
static int _rar_nav_get_depth(const wchar_t *filenamew, const size_t file_size);
static int _rar_nav_compare_entries(const void *op1, const void *op2 TSRMLS_DC);
#if PHP_MAJOR_VERSION >= 7
static void _rar_nav_swap_entries(void *op1, void *op2);
#endif
static int _rar_nav_compare_entries_std(const void *op1, const void *op2);
static inline int _rar_nav_compare_values(const wchar_t *str1, const int depth1,
const wchar_t *str2, const int depth2,
@@ -110,9 +116,15 @@ void _rar_entry_search_start(rar_file_t *rar,
sizeof rar->entries->entries_array_s[0]);
memcpy(rar->entries->entries_array_s, rar->entries->entries_array,
rar->entries->num_entries * sizeof rar->entries->entries_array[0]);
#if PHP_MAJOR_VERSION < 7
zend_qsort(rar->entries->entries_array_s, rar->entries->num_entries,
sizeof *rar->entries->entries_array_s, _rar_nav_compare_entries
TSRMLS_CC);
#else
zend_qsort(rar->entries->entries_array_s, rar->entries->num_entries,
sizeof *rar->entries->entries_array_s, _rar_nav_compare_entries,
_rar_nav_swap_entries);
#endif
}
}
/* }}} */
@@ -324,7 +336,7 @@ int _rar_list_files(rar_file_t *rar TSRMLS_DC) /* {{{ */
int result = 0;
size_t capacity = 0;
int first_file_check = TRUE;
zend_ulong packed_size = 0;
unsigned long packed_size = 0UL;
struct _rar_entries *ents;
if (rar->entries != NULL) {
@@ -366,16 +378,22 @@ int _rar_list_files(rar_file_t *rar TSRMLS_DC) /* {{{ */
/* reset packed size if not split before */
if ((entry.Flags & RHDF_SPLITBEFORE) == 0)
packed_size = 0;
packed_size = 0UL;
/* accumulate packed size; cap at ZEND_LONG_MAX (the PHP int ceiling) */
{
zend_ulong entry_packed = ((zend_ulong)entry.PackSizeHigh << 32) | entry.PackSize;
if (entry_packed > (zend_ulong)ZEND_LONG_MAX ||
packed_size > (zend_ulong)ZEND_LONG_MAX - entry_packed)
packed_size = (zend_ulong)ZEND_LONG_MAX;
else
packed_size += entry_packed;
/* we would exceed size of ulong. cap at ulong_max
* equivalent to packed_size + entry.PackSize > ULONG_MAX,
* but without overflowing */
if (ULONG_MAX - packed_size < entry.PackSize)
packed_size = ULONG_MAX;
else {
packed_size += entry.PackSize;
if (entry.PackSizeHigh != 0) {
#if ULONG_MAX > 0xffffffffUL
packed_size += ((unsigned long) entry.PackSizeHigh) << 32;
#else
packed_size = ULONG_MAX; /* cap */
#endif
}
}
if (entry.Flags & RHDF_SPLITAFTER) /* do not commit */
@@ -484,6 +502,7 @@ static int _rar_nav_compare_entries(const void *op1, const void *op2 TSRMLS_DC)
}
/* }}} */
#if PHP_MAJOR_VERSION >= 7
static void _rar_nav_swap_entries(void *op1, void *op2) /* {{{ */
{
/* just swaps two pointer values */
@@ -496,6 +515,7 @@ static void _rar_nav_swap_entries(void *op1, void *op2) /* {{{ */
}
/* }}} */
#endif
static int _rar_nav_compare_entries_std(const void *op1, const void *op2) /* {{{ */
{
@@ -600,6 +620,12 @@ static size_t _rar_nav_position_on_dir_start(const wchar_t *dir_name,
/* end functions with internal linkage */
#endif /* HAVE_RAR */
#ifdef __cplusplus
}
#endif
/*
* Local variables:
* tab-width: 4
@@ -608,3 +634,5 @@ static size_t _rar_nav_position_on_dir_start(const wchar_t *dir_name,
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@@ -27,11 +27,17 @@
/* $Id$ */
#ifdef HAVE_CONFIG_H
# include "config.h"
# include "config.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <php.h>
#if HAVE_RAR
#include <wchar.h>
#include "php_rar.h"
@@ -41,7 +47,6 @@
#include <ext/standard/php_string.h>
#include <ext/standard/file.h>
/* clang-format off */
typedef struct php_rar_stream_data_t {
struct RAROpenArchiveDataEx open_data;
struct RARHeaderDataEx header_data;
@@ -68,15 +73,12 @@ typedef struct php_rar_dir_stream_data_t {
int no_encode; /* do not urlencode entry names */
php_stream *stream;
} php_rar_dir_stream_data, *php_rar_dir_stream_data_P;
/* clang-format on */
/* clang-format off */
#define STREAM_DATA_FROM_STREAM \
php_rar_stream_data_P self = (php_rar_stream_data_P) stream->abstract;
#define STREAM_DIR_DATA_FROM_STREAM \
php_rar_dir_stream_data_P self = (php_rar_dir_stream_data_P) stream->abstract;
/* clang-format on */
/* len can be -1 (calculate) */
static char *_rar_wide_to_utf_with_alloc(const wchar_t *wide, int len)
@@ -164,7 +166,7 @@ static ssize_t php_rar_ops_read(php_stream *stream, char *buf, size_t count)
if (self->cursor > self->file_size) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"The file size is supposed to be %lu bytes, but "
"we read more: %" PRIu64 " bytes (corruption/wrong pwd)",
"we read more: %lu bytes (corruption/wrong pwd)",
self->file_size, self->cursor);
}
}
@@ -177,7 +179,11 @@ static ssize_t php_rar_ops_read(php_stream *stream, char *buf, size_t count)
stream->eof = 1;
}
#if PHP_VERSION_ID < 50400
return n;
#else
return (ssize_t) n;
#endif
}
/* }}} */
@@ -418,10 +424,19 @@ static ssize_t php_rar_dir_ops_read(php_stream *stream, char *buf, size_t count
entry.d_name, sizeof entry.d_name);
if (!self->no_encode) { /* urlencode entry */
#if PHP_MAJOR_VERSION < 7
int new_len;
char *encoded_name;
encoded_name = php_url_encode(entry.d_name, strlen(entry.d_name),
&new_len);
strlcpy(entry.d_name, encoded_name, sizeof entry.d_name);
efree(encoded_name);
#else
zend_string *encoded_name =
php_url_encode(entry.d_name, strlen(entry.d_name));
strlcpy(entry.d_name, encoded_name->val, sizeof entry.d_name);
zend_string_release(encoded_name);
#endif
}
@@ -437,7 +452,11 @@ static int php_rar_dir_ops_close(php_stream *stream, int close_handle TSRMLS_DC)
{
STREAM_DIR_DATA_FROM_STREAM
#if PHP_MAJOR_VERSION < 7
zval_dtor(&self->rar_obj);
#else
zval_ptr_dtor(&self->rar_obj);
#endif
efree(self->directory);
efree(self->state);
efree(self);
@@ -568,6 +587,67 @@ cleanup:
/* {{{ Wrapper stuff */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3
/* PHP 5.2 has no zend_resolve_path. Adapted from 5.3's php_resolve_path */
static char *zend_resolve_path(const char *filename,
int filename_length TSRMLS_DC) /* {{{ */
{
const char *path = PG(include_path);
char resolved_path[MAXPATHLEN];
char trypath[MAXPATHLEN];
const char *ptr, *end;
char *actual_path;
if (filename == NULL || filename[0] == '\0') {
return NULL;
}
/* do not use the include path in these circumstances */
if ((*filename == '.' && (IS_SLASH(filename[1]) ||
((filename[1] == '.') && IS_SLASH(filename[2])))) ||
IS_ABSOLUTE_PATH(filename, filename_length) ||
path == NULL || path[0] == '\0') {
if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) {
return estrdup(resolved_path);
} else {
return NULL;
}
}
ptr = path;
while (ptr && *ptr) {
end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
if (end) {
if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
ptr = end + 1;
continue;
}
memcpy(trypath, ptr, end-ptr);
trypath[end-ptr] = '/';
memcpy(trypath+(end-ptr)+1, filename, filename_length+1);
ptr = end+1;
} else {
int len = strlen(ptr);
if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
break;
}
memcpy(trypath, ptr, len);
trypath[len] = '/';
memcpy(trypath+len+1, filename, filename_length+1);
ptr = NULL;
}
actual_path = trypath;
if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
return estrdup(resolved_path);
}
} /* end provided path */
return NULL;
}
/* }}} */
#endif
/* {{{ php_rar_process_context */
/* memory is to be managed externally */
static void php_rar_process_context(php_stream_context *context,
@@ -578,6 +658,9 @@ static void php_rar_process_context(php_stream_context *context,
zval **volume_cb TSRMLS_DC)
{
zval *ctx_opt;
#if PHP_MAJOR_VERSION < 7
zval **ctx_opt_p = NULL;
#endif
assert(context != NULL);
assert(open_password != NULL);
@@ -587,8 +670,14 @@ static void php_rar_process_context(php_stream_context *context,
/* TODO: don't know if I can log errors and not fail. check that */
#if PHP_MAJOR_VERSION < 7
if (php_stream_context_get_option(
context, "rar", "open_password", &ctx_opt_p) == SUCCESS) {
ctx_opt = *ctx_opt_p;
#else
if ((ctx_opt = php_stream_context_get_option(
context, "rar", "open_password"))) {
#endif
if (Z_TYPE_P(ctx_opt) != IS_STRING)
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
"RAR open password was provided, but not a string.");
@@ -596,8 +685,14 @@ static void php_rar_process_context(php_stream_context *context,
*open_password = Z_STRVAL_P(ctx_opt);
}
#if PHP_MAJOR_VERSION < 7
if (file_password != NULL && php_stream_context_get_option(context, "rar",
"file_password", &ctx_opt_p) == SUCCESS) {
ctx_opt = *ctx_opt_p;
#else
if (file_password != NULL && (ctx_opt = php_stream_context_get_option(
context, "rar", "file_password"))) {
#endif
if (Z_TYPE_P(ctx_opt) != IS_STRING)
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC,
"RAR file password was provided, but not a string.");
@@ -605,9 +700,19 @@ static void php_rar_process_context(php_stream_context *context,
*file_password = Z_STRVAL_P(ctx_opt);
}
#if PHP_MAJOR_VERSION < 7
if (php_stream_context_get_option(context, "rar", "volume_callback",
&ctx_opt_p) == SUCCESS) {
ctx_opt = *ctx_opt_p;
#else
if ((ctx_opt = php_stream_context_get_option(
context, "rar", "volume_callback"))) {
#endif
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2
if (zend_is_callable(ctx_opt, IS_CALLABLE_STRICT, NULL)) {
#else
if (zend_is_callable(ctx_opt, IS_CALLABLE_STRICT, NULL TSRMLS_CC)) {
#endif
*volume_cb = ctx_opt;
}
else
@@ -676,19 +781,23 @@ static int _rar_get_archive_and_fragment(php_stream_wrapper *wrapper,
if (!(options & STREAM_ASSUME_REALPATH)) {
if (options & USE_PATH) {
#if PHP_VERSION_ID < 80100
zend_string *arc_str = zend_resolve_path(tmp_archive, tmp_arch_len);
#if PHP_MAJOR_VERSION < 7
*archive = zend_resolve_path(tmp_archive, tmp_arch_len TSRMLS_CC);
#else
# if PHP_VERSION_ID < 80100
zend_string *arc_str = zend_resolve_path(tmp_archive, tmp_arch_len);
# else
zend_string *tmp_archive_str = zend_string_init_fast(tmp_archive, tmp_arch_len);
zend_string *arc_str = zend_resolve_path(tmp_archive_str);
zend_string_free(tmp_archive_str);
#endif
# endif
if (arc_str != NULL) {
*archive = estrndup(arc_str->val, arc_str->len);
} else {
*archive = NULL;
}
zend_string_release(arc_str);
#endif
}
if (*archive == NULL) {
if ((*archive = expand_filepath(tmp_archive, NULL TSRMLS_CC))
@@ -756,10 +865,17 @@ cleanup:
/* {{{ php_stream_rar_opener */
static php_stream *php_stream_rar_opener(php_stream_wrapper *wrapper,
#if PHP_MAJOR_VERSION < 7
char *filename,
char *mode,
int options,
char **opened_path,
#else
const char *filename,
const char *mode,
int options,
zend_string **opened_path,
#endif
php_stream_context *context
STREAMS_DC TSRMLS_DC)
{
@@ -871,8 +987,12 @@ cleanup:
if (tmp_open_path != NULL) {
if (opened_path != NULL) {
#if PHP_MAJOR_VERSION < 7
*opened_path = tmp_open_path;
#else
*opened_path =
zend_string_init(tmp_open_path, strlen(tmp_open_path), 0);
#endif
} else {
efree(tmp_open_path);
}
@@ -928,7 +1048,11 @@ static int _rar_get_cachable_rararch(php_stream_wrapper *wrapper,
zval *cache_zv;
assert(rar_obj != NULL);
#if PHP_MAJOR_VERSION < 7
INIT_ZVAL(*rar_obj);
#else
ZVAL_UNDEF(rar_obj);
#endif
_rar_arch_cache_get_key(arch_path, open_passwd, volume_cb, &cache_key,
&cache_key_len);
@@ -991,26 +1115,56 @@ cleanup:
efree(cache_key);
if (ret != SUCCESS && Z_TYPE_P(rar_obj) == IS_OBJECT) {
#if PHP_MAJOR_VERSION < 7
zval_dtor(rar_obj);
Z_TYPE_P(rar_obj) = IS_NULL;
#else
zval_ptr_dtor(rar_obj);
ZVAL_UNDEF(rar_obj);
#endif
}
return ret;
}
/* }}} */
/* {{{ _rar_stream_tidy_wrapper_error_log */
/* {{{ _rar_stream_tidy_wrapper_error_log
* These two different versions are because of PHP commit 7166298 */
#if PHP_VERSION_ID <= 50310 || PHP_VERSION_ID == 50400
/* copied from main/streams/streams.c because it's an internal function */
static void _rar_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
{
if (wrapper) {
/* tidy up the error stack */
int i;
for (i = 0; i < wrapper->err_count; i++) {
efree(wrapper->err_stack[i]);
}
if (wrapper->err_stack) {
efree(wrapper->err_stack);
}
wrapper->err_stack = NULL;
wrapper->err_count = 0;
}
}
#else
static void _rar_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC)
{
if (wrapper && FG(wrapper_errors)) {
zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof wrapper);
}
}
#endif
/* }}} */
/* {{{ php_stream_rar_stater */
static int php_stream_rar_stater(php_stream_wrapper *wrapper,
#if PHP_MAJOR_VERSION < 7
char *url,
#else
const char *url,
#endif
int flags,
php_stream_statbuf *ssb,
php_stream_context *context TSRMLS_DC)
@@ -1029,7 +1183,11 @@ static int php_stream_rar_stater(php_stream_wrapper *wrapper,
int ret = FAILURE;
/* {{{ preliminaries */
#if PHP_MAJOR_VERSION < 7
Z_TYPE(rararch) = IS_NULL;
#else
ZVAL_UNDEF(&rararch);
#endif
if (_rar_get_archive_and_fragment(wrapper, url, options, 1,
&open_path, &fragment, NULL TSRMLS_CC) == FAILURE) {
@@ -1080,7 +1238,11 @@ cleanup:
}
if (Z_TYPE(rararch) == IS_OBJECT) {
#if PHP_MAJOR_VERSION < 7
zval_dtor(&rararch);
#else
zval_ptr_dtor(&rararch);
#endif
}
if (state != NULL) {
_rar_entry_search_end(state);
@@ -1103,10 +1265,17 @@ cleanup:
/* {{{ php_stream_rar_dir_opener */
static php_stream *php_stream_rar_dir_opener(php_stream_wrapper *wrapper,
#if PHP_MAJOR_VERSION < 7
char *filename,
char *mode,
int options,
char **opened_path,
#else
const char *filename,
const char *mode,
int options,
zend_string **opened_path,
#endif
php_stream_context *context
STREAMS_DC TSRMLS_DC)
{
@@ -1205,8 +1374,12 @@ cleanup:
if (tmp_open_path != NULL) {
if (opened_path != NULL) {
#if PHP_MAJOR_VERSION < 7
*opened_path = tmp_open_path;
#else
*opened_path =
zend_string_init(tmp_open_path, strlen(tmp_open_path), 0);
#endif
} else {
efree(tmp_open_path);
}
@@ -1217,7 +1390,11 @@ cleanup:
if (stream == NULL) { /* failed */
if (self != NULL) {
if (Z_TYPE(self->rar_obj) == IS_OBJECT) {
#if PHP_MAJOR_VERSION < 7
zval_dtor(&self->rar_obj);
#else
zval_ptr_dtor(&self->rar_obj);
#endif
}
if (self->directory != NULL) {
efree(self->directory);
@@ -1254,6 +1431,12 @@ php_stream_wrapper php_stream_rar_wrapper = {
/* end wrapper stuff }}} */
#endif /* HAVE_RAR */
#ifdef __cplusplus
}
#endif
/*
* Local variables:
* tab-width: 4

View File

@@ -1,3 +1,7 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <php.h>
#include "php_rar.h"
@@ -53,3 +57,7 @@ int rar_dos_time_convert(unsigned dos_time, time_t *to) /* {{{ */
return SUCCESS;
}
/* }}} */
#ifdef __cplusplus
}
#endif

218
rararch.c
View File

@@ -29,6 +29,9 @@
#include "zend_types.h"
#include <zend_API.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
@@ -42,19 +45,26 @@
#include "php_compat.h"
/* {{{ Type definitions reserved for this translation unit */
/* clang-format off */
typedef struct _ze_rararch_object {
#if PHP_MAJOR_VERSION < 7
zend_object parent;
rar_file_t *rar_file;
#else
rar_file_t *rar_file;
zend_object parent;
#endif
} ze_rararch_object;
typedef struct _rararch_iterator {
zend_object_iterator parent;
rar_find_output *state;
#if PHP_MAJOR_VERSION < 7
zval *value;
#else
zval value;
#endif
int empty_iterator; /* iterator should give nothing */
} rararch_iterator;
/* clang-format on */
/* }}} */
/* {{{ Globals with internal linkage */
@@ -78,19 +88,30 @@ static zend_object_handlers rararch_object_handlers;
/* {{{ Function prototypes for functions with internal linkage */
static inline rar_obj_ref rar_obj_ref_fetch(zval *zv);
static inline void rar_obj_ref_make_zv(rar_obj_ref zo, zval *zv TSRMLS_DC);
#if PHP_MAJOR_VERSION >= 7
static inline ze_rararch_object *rararch_object_fetch(zend_object *zobj);
static ze_rararch_object *rararch_object_from_zv(const zval *zv);
static ze_rararch_object *rararch_object_from_ref(const rar_obj_ref ref);
static zend_object *rararch_ce_create_object(zend_class_entry *ce);
static void rararch_ce_free_object_storage(zend_object *zobj);
#else
#define rararch_object_from_zv zend_object_store_get_object
#define rararch_object_from_ref(ref) zend_object_store_get_object_by_handle((ref) TSRMLS_CC)
static zend_object_value rararch_ce_create_object(zend_class_entry *class_type TSRMLS_DC);
static void rararch_ce_free_object_storage(ze_rararch_object *object TSRMLS_DC);
#endif
/* }}} */
/* {{{ RarArchive handlers */
static int rararch_handlers_preamble(handler_this_t *object, rar_file_t **rar TSRMLS_DC);
static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, zend_long *index, int quiet TSRMLS_DC);
static int rararch_count_elements(handler_this_t *object, zend_long *count TSRMLS_DC);
static int rararch_dimensions_preamble(rar_file_t *rar, zval *offset, long *index, int quiet TSRMLS_DC);
static int rararch_count_elements(handler_this_t *object, long *count TSRMLS_DC);
#if PHP_MAJOR_VERSION < 7
static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
#else
static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int type, zval *rv);
#endif
static void rararch_write_dimension(handler_this_t *object, zval *offset, zval *value TSRMLS_DC);
static int rararch_has_dimension(handler_this_t *object, zval *offset, int check_empty TSRMLS_DC);
/* }}} */
@@ -237,17 +258,25 @@ static void _rar_raw_entries_to_array(rar_file_t *rar, zval *target TSRMLS_DC) /
state->position, entry_obj TSRMLS_CC);
add_next_index_zval(target, entry_obj);
#if PHP_MAJOR_VERSION >= 7
/* PHP 7 copies the zval (but without increasing the refcount of the
* obj). Free the allocation. */
* obj), while 5.x simply copies the pointer. Only for PHP 5.x do we
* keep the allocation) */
efree(entry_obj);
#endif
}
} while (state->eof == 0);
_rar_entry_search_end(state);
#if PHP_MAJOR_VERSION < 7
zval_dtor(&rararch_obj);
#else
zval_ptr_dtor(&rararch_obj);
#endif
}
/* }}} */
#if PHP_MAJOR_VERSION >=7
static inline rar_obj_ref rar_obj_ref_fetch(zval *zv)
{
return Z_OBJ(*zv);
@@ -257,7 +286,24 @@ static inline void rar_obj_ref_make_zv(rar_obj_ref zo, zval *zv TSRMLS_DC)
ZVAL_OBJ(zv, zo);
zval_addref_p(zv);
}
#else
inline rar_obj_ref rar_obj_ref_fetch(zval *zv)
{
return Z_OBJ_HANDLE_P(zv);
}
inline void rar_obj_ref_make_zv(rar_obj_ref zoh, zval *zv TSRMLS_DC)
{
INIT_ZVAL(*zv);
Z_TYPE_P(zv) = IS_OBJECT;
Z_OBJ_HANDLE_P(zv) = zoh;
Z_OBJ_HT_P(zv) = &rararch_object_handlers;
/* object has a new reference; if not incremented, the object would be
* be destroyed when this new zval we created was destroyed */
zend_objects_store_add_ref_by_handle(zoh TSRMLS_CC);
}
#endif
#if PHP_MAJOR_VERSION >=7
static inline ze_rararch_object *rararch_object_fetch(zend_object *zobj)
{
return (ze_rararch_object *)
@@ -271,8 +317,35 @@ static ze_rararch_object *rararch_object_from_ref(const rar_obj_ref ref)
{
return rararch_object_fetch(ref);
}
#endif
/* {{{ */
#if PHP_MAJOR_VERSION < 7
static zend_object_value rararch_ce_create_object(zend_class_entry *class_type TSRMLS_DC)
{
zend_object_value zov;
ze_rararch_object *zobj;
zobj = emalloc(sizeof *zobj);
/* rararch_ce_free_object_storage will attempt to access it otherwise */
zobj->rar_file = NULL;
zend_object_std_init((zend_object*) zobj, class_type TSRMLS_CC);
#if PHP_VERSION_ID < 50399
zend_hash_copy(((zend_object*)zobj)->properties,
&(class_type->default_properties),
(copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
#else
object_properties_init((zend_object*)zobj, class_type);
#endif
zov.handle = zend_objects_store_put(zobj,
(zend_objects_store_dtor_t) zend_objects_destroy_object,
(zend_objects_free_object_storage_t) rararch_ce_free_object_storage,
NULL TSRMLS_CC);
zov.handlers = &rararch_object_handlers;
return zov;
}
#else
static zend_object *rararch_ce_create_object(zend_class_entry *ce)
{
ze_rararch_object *zobj =
@@ -284,12 +357,18 @@ static zend_object *rararch_ce_create_object(zend_class_entry *ce)
return &zobj->parent;
}
#endif
/* }}} */
/* {{{ */
#if PHP_MAJOR_VERSION < 7
static void rararch_ce_free_object_storage(ze_rararch_object *object TSRMLS_DC)
{
#else
static void rararch_ce_free_object_storage(zend_object *zobj)
{
ze_rararch_object *object = rararch_object_fetch(zobj);
#endif
rar_file_t *rar = object->rar_file;
/* may be NULL if the user did new RarArchive() */
@@ -313,6 +392,9 @@ static void rararch_ce_free_object_storage(zend_object *zobj)
/* could call zend_objects_free_object_storage here (not before!), but
* instead I'll mimic its behaviour */
zend_object_std_dtor(&object->parent TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
efree(object);
#endif
}
/* }}} */
@@ -338,7 +420,7 @@ static int rararch_handlers_preamble(handler_this_t *object,
/* {{{ rararch_dimensions_preamble - semi-strict parsing of int argument */
static int rararch_dimensions_preamble(rar_file_t *rar,
zval *offset,
zend_long *index,
long *index,
int quiet TSRMLS_DC)
{
if (offset == NULL) {
@@ -362,32 +444,36 @@ static int rararch_dimensions_preamble(rar_file_t *rar,
return FAILURE;
}
else if (type == IS_DOUBLE) {
if (d > (double) ZEND_LONG_MAX || d < (double) ZEND_LONG_MIN) {
if (d > (double) LONG_MAX || d < (double) LONG_MIN) {
RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING,
"Dimension index is out of integer bounds");
return FAILURE;
}
*index = (zend_long) d;
*index = (long) d;
}
}
else if (Z_TYPE_P(offset) == IS_DOUBLE) {
if (Z_DVAL_P(offset) > (double) ZEND_LONG_MAX ||
Z_DVAL_P(offset) < (double) ZEND_LONG_MIN) {
if (Z_DVAL_P(offset) > (double) LONG_MAX ||
Z_DVAL_P(offset) < (double) LONG_MIN) {
RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING,
"Dimension index is out of integer bounds");
return FAILURE;
}
*index = (zend_long) Z_DVAL_P(offset);
*index = (long) Z_DVAL_P(offset);
}
else if (Z_TYPE_P(offset) == IS_OBJECT) {
#if PHP_MAJOR_VERSION < 8
if (Z_OBJ_HT_P(offset)->get) {
zval *newoffset = NULL;
int ret;
# if PHP_MAJOR_VERSION < 7
newoffset = Z_OBJ_HT_P(offset)->get(offset TSRMLS_CC);
# else
zval zv_holder;
ZVAL_NULL(&zv_holder);
newoffset = Z_OBJ_HT_P(offset)->get(offset, &zv_holder);
# endif
/* get handler cannot return NULL */
assert(newoffset != NULL);
@@ -400,7 +486,11 @@ static int rararch_dimensions_preamble(rar_file_t *rar,
ret = rararch_dimensions_preamble(rar, newoffset, index, quiet
TSRMLS_CC);
# if PHP_MAJOR_VERSION < 7
zval_ptr_dtor(&newoffset);
# else
zval_ptr_dtor(newoffset);
# endif
return ret;
} else
#endif // PHP < 8
@@ -437,9 +527,9 @@ static int rararch_dimensions_preamble(rar_file_t *rar,
return FAILURE;
}
if (*index < 0) {
if (*index < 0L) {
RAR_DOCREF_IF_UNQUIET(NULL TSRMLS_CC, E_WARNING,
"Dimension index must be non-negative, given " ZEND_LONG_FMT, *index);
"Dimension index must be non-negative, given %ld", *index);
return FAILURE;
}
if ((size_t) *index >= _rar_entry_count(rar)) {
@@ -454,19 +544,20 @@ static int rararch_dimensions_preamble(rar_file_t *rar,
/* }}} */
/* {{{ RarArchive count_elements handler */
static int rararch_count_elements(handler_this_t *object, zend_long *count TSRMLS_DC)
static int rararch_count_elements(zend_object *object, zend_long *count TSRMLS_DC)
{
rar_file_t *rar = NULL;
size_t entry_count;
if (rararch_handlers_preamble(object, &rar TSRMLS_CC) == FAILURE) {
*count = 0;
*count = 0L;
return SUCCESS; /* intentional */
}
entry_count = _rar_entry_count(rar);
if (entry_count > ZEND_LONG_MAX)
entry_count = (size_t) ZEND_LONG_MAX;
entry_count = ZEND_LONG_MAX;
*count = (zend_long) entry_count;
@@ -475,9 +566,13 @@ static int rararch_count_elements(handler_this_t *object, zend_long *count TSRML
/* }}} */
/* {{{ RarArchive read_dimension handler */
#if PHP_MAJOR_VERSION < 7
static zval *rararch_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
#else
static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int type, zval *rv)
#endif
{
zend_long index;
long index;
rar_file_t *rar = NULL;
struct _rar_find_output *out;
zval *ret = NULL;
@@ -498,7 +593,11 @@ static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int ty
_rar_entry_search_seek(out, (size_t) index);
_rar_entry_search_advance(out, NULL, 0, 0);
assert(out->found);
#if PHP_MAJOR_VERSION < 7
ALLOC_INIT_ZVAL(ret);
#else
ret = rv;
#endif
#if PHP_MAJOR_VERSION >= 8
zval object_zv;
ZVAL_OBJ(&object_zv, object);
@@ -510,6 +609,9 @@ static zval *rararch_read_dimension(handler_this_t *object, zval *offset, int ty
ret TSRMLS_CC);
#endif
_rar_entry_search_end(out);
#if PHP_MAJOR_VERSION < 7
Z_DELREF_P(ret); /* set refcount to 0 */
#endif
return ret;
}
/* }}} */
@@ -525,7 +627,7 @@ static void rararch_write_dimension(handler_this_t *object, zval *offset, zval *
/* {{{ RarArchive has_dimension handler */
static int rararch_has_dimension(handler_this_t *object, zval *offset, int check_empty TSRMLS_DC)
{
zend_long index;
long index;
rar_file_t *rar = NULL;
(void) check_empty; /* don't care */
@@ -580,7 +682,11 @@ PHP_FUNCTION(rar_open)
assert(strnlen(resolved_path, MAXPATHLEN) < MAXPATHLEN);
if (callable != NULL) { /* given volume resolver callback */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 2
if (!zend_is_callable(callable, IS_CALLABLE_STRICT, NULL)) {
#else
if (!zend_is_callable(callable, IS_CALLABLE_STRICT, NULL TSRMLS_CC)) {
#endif
_rar_handle_ext_error("%s" TSRMLS_CC, "Expected the third "
"argument, if provided, to be a valid callback");
RETURN_FALSE;
@@ -874,7 +980,6 @@ ZEND_END_ARG_INFO()
#endif
/* }}} */
/* clang-format off */
static zend_function_entry php_rararch_class_functions[] = {
PHP_ME_MAPPING(open, rar_open, arginfo_rararchive_open, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC)
PHP_ME_MAPPING(getEntries, rar_list, arginfo_rararchive_void, ZEND_ACC_PUBLIC)
@@ -894,7 +999,6 @@ static zend_function_entry php_rararch_class_functions[] = {
#endif
{NULL, NULL, NULL}
};
/* clang-format on */
/* {{{ Iteration. Very boring stuff indeed. */
@@ -906,7 +1010,12 @@ static zend_object_iterator *rararch_it_get_iterator(zend_class_entry *ce,
static void rararch_it_dtor(zend_object_iterator *iter TSRMLS_DC);
static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC);
static int rararch_it_valid(zend_object_iterator *iter TSRMLS_DC);
#if PHP_MAJOR_VERSION < 7
static void rararch_it_current_data(zend_object_iterator *iter,
zval ***data TSRMLS_DC);
#else
static zval *rararch_it_current_data(zend_object_iterator *iter);
#endif
static void rararch_it_move_forward(zend_object_iterator *iter TSRMLS_DC);
static void rararch_it_rewind(zend_object_iterator *iter TSRMLS_DC);
/* }}} */
@@ -915,8 +1024,15 @@ static void rararch_it_rewind(zend_object_iterator *iter TSRMLS_DC);
static void rararch_it_invalidate_current(zend_object_iterator *iter TSRMLS_DC)
{
rararch_iterator *it = (rararch_iterator *) iter;
#if PHP_MAJOR_VERSION < 7
if (it->value != NULL) {
zval_ptr_dtor(&it->value);
it->value = NULL;
}
#else
zval_ptr_dtor(&it->value);
ZVAL_UNDEF(&it->value);
#endif
}
/* }}} */
@@ -927,9 +1043,16 @@ static void rararch_it_dtor(zend_object_iterator *iter TSRMLS_DC)
rararch_it_invalidate_current((zend_object_iterator *) it TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
zval_ptr_dtor((zval**) &it->parent.data); /* decrease refcount on zval object */
#else
zval_ptr_dtor(&it->parent.data);
#endif
_rar_entry_search_end(it->state);
#if PHP_MAJOR_VERSION < 7
efree(it);
#endif
}
/* }}} */
@@ -940,14 +1063,27 @@ static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC)
int res;
zval *robj;
#if PHP_MAJOR_VERSION < 7
assert(it->value == NULL);
#else
assert(Z_TYPE(it->value) == IS_UNDEF);
#endif
if (it->empty_iterator) {
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(it->value);
ZVAL_FALSE(it->value);
#else
ZVAL_FALSE(&it->value);
#endif
return;
}
#if PHP_MAJOR_VERSION < 7
robj = it->parent.data;
#else
robj = &it->parent.data;
#endif
res = _rar_get_file_resource_zv_ex(robj, &rar_file, 1 TSRMLS_CC);
if (res == FAILURE)
@@ -955,25 +1091,50 @@ static void rararch_it_fetch(rararch_iterator *it TSRMLS_DC)
"Cannot fetch RarArchive object");
_rar_entry_search_advance(it->state, NULL, 0, 0);
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(it->value);
if (it->state->found)
_rar_entry_to_zval(robj, it->state->header, it->state->packed_size,
it->state->position, it->value TSRMLS_CC);
else {
ZVAL_FALSE(it->value);
}
#else
if (it->state->found)
_rar_entry_to_zval(&it->parent.data, it->state->header,
it->state->packed_size, it->state->position, &it->value TSRMLS_CC);
else {
ZVAL_FALSE(&it->value);
}
#endif
}
/* }}} */
/* {{{ rararch_it_valid */
static int rararch_it_valid(zend_object_iterator *iter TSRMLS_DC)
{
#if PHP_MAJOR_VERSION < 7
zval *value = ((rararch_iterator *) iter)->value;
assert(value != NULL);
return (Z_TYPE_P(value) != IS_BOOL)?SUCCESS:FAILURE;
#else
zval *value = &((rararch_iterator *) iter)->value;
assert(Z_TYPE_P(value) != IS_UNDEF);
return Z_TYPE_P(value) != IS_FALSE ? SUCCESS : FAILURE;
#endif
}
/* }}} */
/* {{{ rararch_it_current_data */
#if PHP_MAJOR_VERSION < 7
static void rararch_it_current_data(zend_object_iterator *iter,
zval ***data TSRMLS_DC)
{
zval **value = &(((rararch_iterator *) iter)->value);
assert(*value != NULL);
*data = value;
}
#else
static zval *rararch_it_current_data(zend_object_iterator *iter)
{
zval *ret;
@@ -981,6 +1142,7 @@ static zval *rararch_it_current_data(zend_object_iterator *iter)
assert(Z_TYPE_P(ret) != IS_UNDEF);
return ret;
}
#endif
/* }}} */
/* {{{ rararch_it_move_forward */
@@ -988,7 +1150,11 @@ static void rararch_it_move_forward(zend_object_iterator *iter TSRMLS_DC)
{
rararch_iterator *it = (rararch_iterator *) iter;
rararch_it_invalidate_current((zend_object_iterator *) it TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
it->value = NULL;
#else
ZVAL_UNDEF(&it->value);
#endif
rararch_it_fetch(it TSRMLS_CC);
}
/* }}} */
@@ -1040,9 +1206,15 @@ static zend_object_iterator *rararch_it_get_iterator(zend_class_entry *ce,
rararch_iterator *it = emalloc(sizeof *it);
#if PHP_MAJOR_VERSION < 7
zval_add_ref(&object);
it->parent.data = object;
it->value = NULL;
#else
zend_iterator_init((zend_object_iterator *) it);
ZVAL_COPY(&it->parent.data, object);
ZVAL_UNDEF(&it->value);
#endif
#if PHP_VERSION_ID < 70300
it->parent.funcs = ce->iterator_funcs.funcs;
@@ -1076,8 +1248,10 @@ void minit_rararch(TSRMLS_D)
rararch_object_handlers.has_dimension = rararch_has_dimension;
rararch_object_handlers.unset_dimension = rararch_unset_dimension;
rararch_object_handlers.clone_obj = NULL;
#if PHP_MAJOR_VERSION >= 7
rararch_object_handlers.free_obj = rararch_ce_free_object_storage;
rararch_object_handlers.offset = XtOffsetOf(ze_rararch_object, parent);
#endif
INIT_CLASS_ENTRY(ce, "RarArchive", php_rararch_class_functions);
rararch_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
@@ -1094,3 +1268,7 @@ void minit_rararch(TSRMLS_D)
zend_class_implements(rararch_ce_ptr TSRMLS_CC, 1, zend_ce_traversable);
#endif
}
#ifdef __cplusplus
}
#endif

View File

@@ -51,11 +51,11 @@ static void _rar_dos_date_to_text(unsigned dos_time, char *date_string);
/* {{{ Functions with external linkage */
/* should be passed the last entry that corresponds to a given file
* only that one has the correct CRC. Still, it may have a wrong packedSize */
/* parent is zval to RarArchive object. The object
/* parent is zval to RarArchive object. The object (not the zval, in PHP 5.x)
* will have its refcount increased */
void _rar_entry_to_zval(zval *parent,
struct RARHeaderDataEx *entry,
zend_ulong packed_size,
unsigned long packed_size,
size_t position,
zval *object TSRMLS_DC)
/* {{{ */
@@ -65,8 +65,19 @@ void _rar_entry_to_zval(zval *parent,
char *filename;
int filename_size,
filename_len;
zend_long unp_size;
#if PHP_MAJOR_VERSION >= 7
zend_long unp_size; /* zval stores PHP ints as zend_long, so use that here */
#else
long unp_size; /* zval stores PHP ints as long, so use that here */
#endif
zval *parent_copy = parent;
#if PHP_MAJOR_VERSION < 7
/* allocate zval on the heap */
zval_addref_p(parent_copy);
SEPARATE_ZVAL(&parent_copy);
/* set refcount to 0; zend_update_property will increase it */
Z_DELREF_P(parent_copy);
#endif
object_init_ex(object, rar_class_entry_ptr);
#if PHP_MAJOR_VERSION >= 8
@@ -78,17 +89,24 @@ void _rar_entry_to_zval(zval *parent,
zend_update_property(rar_class_entry_ptr, obj, "rarfile",
sizeof("rararch") - 1, parent_copy TSRMLS_CC);
{
uint64_t raw_size = (uint64_t)entry->UnpSizeHigh << 32 | entry->UnpSize;
unp_size = raw_size > (uint64_t)ZEND_LONG_MAX
? ZEND_LONG_MAX : (zend_long)raw_size;
}
#if PHP_MAJOR_VERSION >= 7 && ZEND_ENABLE_ZVAL_LONG64
unp_size = ((zend_long) entry->UnpSize) + (((zend_long) entry->UnpSizeHigh) << 32);
#elif PHP_MAJOR_VERSION < 7 && ULONG_MAX > 0xffffffffUL
unp_size = ((long) entry->UnpSize) + (((long) entry->UnpSizeHigh) << 32);
#else
/* for 32-bit long, at least don't give negative values */
if ((unsigned long) entry->UnpSize > (unsigned long) LONG_MAX
|| entry->UnpSizeHigh != 0)
unp_size = LONG_MAX;
else
unp_size = (long) entry->UnpSize;
#endif
filename_size = sizeof(entry->FileNameW) * 4;
filename = (char*) emalloc(filename_size);
if (packed_size > (zend_ulong) ZEND_LONG_MAX)
packed_size = (zend_ulong) ZEND_LONG_MAX;
if (packed_size > (unsigned long) LONG_MAX)
packed_size = LONG_MAX;
_rar_wide_to_utf(entry->FileNameW, filename, filename_size);
/* OK; safe usage below: */
filename_len = _rar_strnlen(filename, filename_size);
@@ -97,7 +115,7 @@ void _rar_entry_to_zval(zval *parent,
* direct call to rarentry_object_handlers.write_property
* zend_update_property_x updates the scope accordingly */
zend_update_property_long(rar_class_entry_ptr, obj, "position",
sizeof("position") - 1, (zend_long) position TSRMLS_CC);
sizeof("position") - 1, (long) position TSRMLS_CC);
zend_update_property_stringl(rar_class_entry_ptr, obj, "name",
sizeof("name") - 1, filename, filename_len TSRMLS_CC);
zend_update_property_long(rar_class_entry_ptr, obj, "unpacked_size",
@@ -164,7 +182,7 @@ void _rar_entry_to_zval(zval *parent,
#define REG_RAR_CLASS_CONST_LONG(const_name, value) \
zend_declare_class_constant_long(rar_class_entry_ptr, const_name, \
sizeof(const_name) - 1, (zend_long) value TSRMLS_CC)
sizeof(const_name) - 1, (long) value TSRMLS_CC)
#define REG_RAR_PROPERTY(name, comment) \
_rar_decl_priv_prop_null(rar_class_entry_ptr, name, sizeof(name) -1, \
@@ -174,6 +192,13 @@ static int _rar_decl_priv_prop_null(zend_class_entry *ce, const char *name,
int name_length, char *doc_comment,
int doc_comment_len TSRMLS_DC) /* {{{ */
{
#if PHP_MAJOR_VERSION < 7
zval *property;
ALLOC_PERMANENT_ZVAL(property);
INIT_ZVAL(*property);
return zend_declare_property_ex(ce, name, name_length, property,
ZEND_ACC_PRIVATE, doc_comment, doc_comment_len TSRMLS_CC);
#else
zval property;
zend_string *name_str,
*doc_str;
@@ -193,13 +218,16 @@ static int _rar_decl_priv_prop_null(zend_class_entry *ce, const char *name,
zend_string_release(name_str);
zend_string_release(doc_str);
return ret;
#endif
}
/* }}} */
static zval *_rar_entry_get_property(zval *entry_obj, char *name, int namelen TSRMLS_DC) /* {{{ */
{
zval *tmp;
#if PHP_MAJOR_VERSION >= 7
zval zv;
#endif
#if PHP_VERSION_ID < 70100
zend_class_entry *orig_scope = EG(scope);
@@ -208,8 +236,10 @@ static zval *_rar_entry_get_property(zval *entry_obj, char *name, int namelen TS
#if PHP_MAJOR_VERSION >= 8
tmp = zend_read_property(Z_OBJCE_P(entry_obj), Z_OBJ_P(entry_obj), name, namelen, 1, &zv);
#else
#elif PHP_MAJOR_VERSION >= 7
tmp = zend_read_property(Z_OBJCE_P(entry_obj), entry_obj, name, namelen, 1, &zv);
#else
tmp = zend_read_property(Z_OBJCE_P(entry_obj), entry_obj, name, namelen, 1 TSRMLS_CC);
#endif
if (tmp == NULL) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
@@ -265,7 +295,7 @@ PHP_METHOD(rarentry, extract)
*tmp_position;
rar_file_t *rar = NULL;
zval *entry_obj = getThis();
struct RARHeaderDataEx entry = {0};
struct RARHeaderDataEx entry;
HANDLE extract_handle = NULL;
int result;
int found;
@@ -561,7 +591,7 @@ PHP_METHOD(rarentry, isDirectory)
{
zval *tmp;
zval *entry_obj = getThis();
zend_long flags;
long flags;
int is_dir;
RAR_RETNULL_ON_ARGS();
@@ -580,7 +610,7 @@ PHP_METHOD(rarentry, isEncrypted)
{
zval *tmp;
zval *entry_obj = getThis();
zend_long flags;
long flags;
int is_encrypted;
RAR_RETNULL_ON_ARGS();
@@ -720,7 +750,6 @@ ZEND_END_ARG_INFO()
#endif
/* }}} */
/* clang-format off */
static zend_function_entry php_rar_class_functions[] = {
PHP_ME(rarentry, extract, arginfo_rarentry_extract, ZEND_ACC_PUBLIC)
PHP_ME(rarentry, getPosition, arginfo_rar_void, ZEND_ACC_PUBLIC)
@@ -743,7 +772,6 @@ static zend_function_entry php_rar_class_functions[] = {
PHP_ME_MAPPING(__construct, rar_bogus_ctor, arginfo_rar_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR)
{NULL, NULL, NULL}
};
/* clang-format on */
void minit_rarentry(TSRMLS_D)
{
@@ -770,7 +798,6 @@ void minit_rarentry(TSRMLS_D)
REG_RAR_PROPERTY("redir_to_directory", "Whether the redirection target is a directory");
REG_RAR_PROPERTY("redir_target", "Target of the redirectory");
/* clang-format off */
REG_RAR_CLASS_CONST_LONG("HOST_MSDOS", HOST_MSDOS);
REG_RAR_CLASS_CONST_LONG("HOST_OS2", HOST_OS2);
REG_RAR_CLASS_CONST_LONG("HOST_WIN32", HOST_WIN32);
@@ -822,5 +849,4 @@ void minit_rarentry(TSRMLS_D)
REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_REGULAR_FILE", 0x08000L);
REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_SYM_LINK", 0x0A000L);
REG_RAR_CLASS_CONST_LONG("ATTRIBUTE_UNIX_SOCKET", 0x0C000L);
/* clang-format on */
}

0
report.xml Normal file
View File

File diff suppressed because it is too large Load Diff

70
test_funcs.sh Normal file
View File

@@ -0,0 +1,70 @@
#!/bin/bash -e
JOBS=3
function package_name {
local readonly version=$1 zts=$2
local zts_suffix=''
if [[ $zts = 'true' ]]; then
zts_suffix='-zts'
fi
echo "php-${version}${zts_suffix}-bare-dbg"
}
function prefix {
local readonly version=$1 zts=$2
echo "/opt/$(package_name $version $zts)"
}
function build_ext {
local readonly version=$1 zts=$2 coverage=$3
local readonly prefix=$(prefix $1 $2)
local cflags= cxxflags= ldflags=
"$prefix"/bin/phpize
if [[ $coverage == true ]]; then
cflags=--coverage
cxxflags=--coverage
ldflags=--coverage
fi
CFLAGS="$cflags" CXXFLAGS="$cxxflags" LDFLAGS="$ldflags" \
./configure --with-php-config="$prefix/bin/php-config"
make -j $JOBS
}
function do_tests {
local readonly prefix=$1
local found_leaks= dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local ret=0
sed -i s/-@if/@if/ Makefile
TEST_PHP_EXECUTABLE="$prefix/bin/php" \
TEST_PHP_JUNIT=report.xml \
REPORT_EXIT_STATUS=1 \
NO_INTERACTION=1 \
TESTS="--set-timeout 300 --show-diff $RUN_TESTS_FLAGS" make test \
|| ret=$?
found_leaks=$(find tests -name '*.mem' | wc -l)
if [[ $found_leaks -gt 0 ]]; then
echo "Found $found_leaks leaks. Failing."
find tests -name "*.mem" -print -exec cat {} \;
fi
return $ret
}
function install_php {
local readonly version=$1 zts=$2
local readonly url="$MIRROR/php-$version.tar.gz"
sudo apt-get install -y gnupg
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 5D98E7264E3F3D89463B314B12229434A9F003C9
echo deb [arch=amd64] http://artefacto-test.s3.amazonaws.com/php-bare-dbg bionic main | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install -y $(package_name $version $zts)
}
function run_tests {
set -e
set -o pipefail
do_tests "$(prefix $1 $2)"
}

View File

@@ -1,10 +1,7 @@
--TEST--
RarEntry::getStream() function (good RAR file, several volumes)
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
?>
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
@@ -12,10 +9,6 @@ $rar_file1 = rar_open(dirname(__FILE__).'/multi.part1.rar');
$entries = rar_list($rar_file1);
echo count($entries)." files:\n\n";
//var_dump($entries);
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
foreach ($entries as $e) {
$stream = $e->getStream();
if ($stream === false) {
@@ -28,7 +21,7 @@ foreach ($entries as $e) {
$a .= fread($stream, 8192);
}
echo strlen($a)." bytes, CRC ";
echo int32_to_hex(crc32($a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo strtoupper(hash("crc32b", $a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
}
echo "Done\n";

View File

@@ -1,10 +1,7 @@
--TEST--
RarEntry::getStream() function (store method)
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
?>
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
@@ -12,10 +9,6 @@ $rar_file1 = rar_open(dirname(__FILE__).'/store_method.rar');
$entries = rar_list($rar_file1);
echo count($entries)." files:\n\n";
//var_dump($entries);
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
foreach ($entries as $e) {
$stream = $e->getStream();
echo $e->getName().": ";
@@ -24,7 +17,7 @@ foreach ($entries as $e) {
$a .= fread($stream, 512);
}
echo strlen($a)." bytes, CRC ";
echo int32_to_hex(crc32($a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo strtoupper(hash("crc32b", $a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
}
echo "Done\n";

View File

@@ -1,10 +1,7 @@
--TEST--
RarEntry::getStream() function (solid archive)
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
?>
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
@@ -12,10 +9,6 @@ $rar_file1 = rar_open(dirname(__FILE__).'/solid.rar');
$entries = rar_list($rar_file1);
echo count($entries)." files:\n\n";
//var_dump($entries);
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
foreach ($entries as $e) {
$stream = $e->getStream();
echo $e->getName().": ";
@@ -24,7 +17,7 @@ foreach ($entries as $e) {
$a .= fread($stream, 8192);
}
echo strlen($a)." bytes, CRC ";
echo int32_to_hex(crc32($a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo strtoupper(hash("crc32b", $a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
}
echo "Done\n";

View File

@@ -4,6 +4,9 @@ RarArchive get iterator on closed file
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$rarF = RarArchive::open(dirname(__FILE__) . '/latest_winrar.rar');
$rarF->close();
foreach ($rarF as $k => $rarE) {
@@ -12,4 +15,4 @@ foreach ($rarF as $k => $rarE) {
}
echo "Done.\n";
--EXPECTF--
Fatal error: main(): The archive is already closed, cannot give an iterator in %s on line %d%A
Fatal error: main(): The archive is already closed, cannot give an iterator in %s on line %d

View File

@@ -1,10 +1,7 @@
--TEST--
RarEntry::getStream() function (broken set fixed with volume callback)
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
?>
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
function resolve($vol) {
@@ -13,10 +10,6 @@ function resolve($vol) {
else
return null;
}
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
$rar_file1 = rar_open(dirname(__FILE__).'/multi_broken.part1.rar', null, 'resolve');
foreach ($rar_file1 as $e) {
$stream = $e->getStream();
@@ -26,7 +19,7 @@ foreach ($rar_file1 as $e) {
$a .= fread($stream, 8192);
}
echo strlen($a)." bytes, CRC ";
echo int32_to_hex(crc32($a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo strtoupper(hash("crc32b", $a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
}
echo "Done\n";

View File

@@ -1,10 +1,7 @@
--TEST--
RarEntry::extract() function (broken set fixed with volume callback)
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
?>
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
function resolve($vol) {
@@ -13,10 +10,6 @@ function resolve($vol) {
else
return null;
}
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
echo "Fail:\n";
$rar_file1 = rar_open(dirname(__FILE__).'/multi_broken.part1.rar');
$entry = $rar_file1->getEntry('file2.txt');
@@ -25,7 +18,7 @@ echo "\nSuccess:\n";
$rar_file1 = rar_open(dirname(__FILE__).'/multi_broken.part1.rar', null, 'resolve');
$entry = $rar_file1->getEntry('file2.txt');
$entry->extract(null, dirname(__FILE__) . "/temp_file2.txt");
echo int32_to_hex(crc32(file_get_contents(dirname(__FILE__) . "/temp_file2.txt")));
echo strtoupper(hash("crc32b", file_get_contents(dirname(__FILE__) . "/temp_file2.txt")));
echo "\n";
echo "Done\n";
?>

View File

@@ -1,14 +1,13 @@
--TEST--
RarArchive::open() volume callback long return (case MAXPATHLEN > MAXPATHSIZE)
RarArchive::open() volume callback long return (case MAXPATHLEN > NM)
--SKIPIF--
<?php
define('MAXPATHSIZE', 0x10000);
if(!extension_loaded("rar")) die("skip");
if (!defined("PHP_MAXPATHLEN"))
define("PHP_MAXPATHLEN", RAR_MAXPATHLEN);
if (!(PHP_MAXPATHLEN > MAXPATHSIZE))
if (!(PHP_MAXPATHLEN > 2048))
die("skip test is for systems where MAXPATHLEN > 2048");
$rp = dirname(__FILE__) . "/" . str_repeat("a", MAXPATHSIZE);
$rp = dirname(__FILE__) . "/" . str_repeat("a", 2048);
if (strlen(dirname(__FILE__) > PHP_MAXPATHLEN - 1))
die("skip current directory is too deep.");
--FILE--
@@ -19,7 +18,7 @@ if (!defined("PHP_MAXPATHLEN"))
chdir(dirname(__FILE__));
$fn = dirname(__FILE__) . '/multi_broken.part1.rar';
function testA($vol) { if ($vol[0] != 'a') return str_repeat("a", MAXPATHSIZE); }
function testA($vol) { if ($vol[0] != 'a') return str_repeat("a", 2048); }
$rar = RarArchive::open($fn, null, 'testA');
$rar->getEntries();

View File

@@ -3,7 +3,6 @@ Stream wrapper with volume find callback
--SKIPIF--
<?php
if(!extension_loaded("rar")) die("skip");
if(PHP_INT_SIZE < 8) die("skip 32-bit PHP not supported");
--FILE--
<?php
function resolve($vol) {
@@ -12,17 +11,13 @@ function resolve($vol) {
else
return null;
}
function int32_to_hex($value) {
$value &= 0xffffffff;
return str_pad(strtoupper(dechex($value)), 8, "0", STR_PAD_LEFT);
}
$stream = fopen("rar://" .
dirname(__FILE__) . '/multi_broken.part1.rar' .
"#file2.txt", "r", false,
stream_context_create(array('rar'=>array('volume_callback'=>'resolve'))));
$a = stream_get_contents($stream);
echo strlen($a)." bytes, CRC ";
echo int32_to_hex(crc32($a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo strtoupper(hash("crc32b", $a))."\n\n"; //you can confirm they're equal to those given by $e->getCrc()
echo "Done.\n";
--EXPECTF--
17704 bytes, CRC F2C79881

15
tests/068.phpt Normal file
View File

@@ -0,0 +1,15 @@
--TEST--
RarArchive direct instantiation does not crash (PHP 5.x)
--SKIPIF--
<?php
if (!extension_loaded("rar")) die("skip");
if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID >= 70000) die("skip for PHP 5.x");
if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do not use with valgrind in PHP <7');
--FILE--
<?php
new RarArchive();
echo "Done\n";
--EXPECTF--
Fatal error: Call to private RarArchive::__construct() from invalid context in %s on line %d

14
tests/069.phpt Normal file
View File

@@ -0,0 +1,14 @@
--TEST--
RarEntry direct instantiation does not crash (PHP 5.x)
--SKIPIF--
<?php if(!extension_loaded("rar")) die("skip");
if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID >= 70000) die("skip for PHP 5.x");
if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do not use with valgrind in PHP <7');
--FILE--
<?php
new RarEntry();
echo "Done\n";
--EXPECTF--
Fatal error: Call to private RarEntry::__construct() from invalid context in %s on line %d

View File

@@ -7,6 +7,9 @@ if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do
?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$f1 = dirname(__FILE__) . "/latest_winrar.rar";
$a = RarArchive::open($f1);
@@ -16,4 +19,4 @@ $a[0] = "jjj";
echo "\n";
echo "Done.\n";
--EXPECTF--
Fatal error: main(): A RarArchive object is not writable in %s on line %d%A
Fatal error: main(): A RarArchive object is not writable in %s on line %d

View File

@@ -4,6 +4,9 @@ RarArchive read_property gives a fatal error on a write context
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$f1 = dirname(__FILE__) . "/latest_winrar.rar";
$a = RarArchive::open($f1);
@@ -18,4 +21,4 @@ unset($a[0]["jj"]);
echo "\n";
echo "Done.\n";
--EXPECTF--
Fatal error: main(): A RarArchive object is not modifiable in %s on line %d%A
Fatal error: main(): A RarArchive object is not modifiable in %s on line %d

View File

@@ -7,6 +7,9 @@ if (key_exists('USE_ZEND_ALLOC', $_ENV) && PHP_VERSION_ID < 70000) die('skip do
?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$f1 = dirname(__FILE__) . "/latest_winrar.rar";
$a = RarArchive::open($f1);
@@ -16,4 +19,4 @@ $a[0] = "hhh";
echo "\n";
echo "Done.\n";
--EXPECTF--
Fatal error: main(): A RarArchive object is not writable in %s on line %d%A
Fatal error: main(): A RarArchive object is not writable in %s on line %d

View File

@@ -4,6 +4,9 @@ RarArchive unset_property gives a fatal error
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$f1 = dirname(__FILE__) . "/latest_winrar.rar";
$a = RarArchive::open($f1);
@@ -13,4 +16,4 @@ unset($a[0]);
echo "\n";
echo "Done.\n";
--EXPECTF--
Fatal error: main(): A RarArchive object is not writable in %s on line %d%A
Fatal error: main(): A RarArchive object is not writable in %s on line %d

View File

@@ -4,6 +4,9 @@ Traversal of RarArchive with foreach by reference gives error
<?php if(!extension_loaded("rar")) print "skip"; ?>
--FILE--
<?php
if (PHP_VERSION_ID>=80500){
ini_set('fatal_error_backtraces', 'Off');
}
$f1 = dirname(__FILE__) . "/latest_winrar.rar";
$a = RarArchive::open($f1);
@@ -15,4 +18,4 @@ foreach ($a as &$v) {
echo "\n";
echo "Done.\n";
--EXPECTF--
Fatal error: main(): An iterator cannot be used with foreach by reference in %s on line %d%A
Fatal error: main(): An iterator cannot be used with foreach by reference in %s on line %d

View File

@@ -2,6 +2,8 @@
Supports version 5 RAR files
--SKIPIF--
<?php if(!extension_loaded("rar")) die("skip");
if (isset($_ENV['APPVEYOR'])) die("skip failing on appveyor");
if ($_ENV['RUNNER_OS'] === 'Windows') die("skip failing on Windows runner on GitHub Action");
--FILE--
<?php
RarException::setUsingExceptions(true);

16
tests/105.phpt Normal file
View File

@@ -0,0 +1,16 @@
--TEST--
Clone of RarArchive is forbidden (PHP 5.x)
--SKIPIF--
<?php if(!extension_loaded("rar")) print "skip";
if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID >= 70000) die("skip for PHP 5.x");
--FILE--
<?php
RarException::setUsingExceptions(true);
$file = dirname(__FILE__) . '/rar5_multi.part1.rar';
$rar = RarArchive::open($file);
$rar2 = clone $rar;
$rar2->getEntries();
echo "Never reached.\n";
?>
--EXPECTF--
Fatal error: Trying to clone an uncloneable object of class RarArchive in %s on line %d

View File

@@ -1,3 +0,0 @@
---
# Disable clang-format for vendored unrar sources.
DisableFormat: true

View File

@@ -238,10 +238,8 @@
<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" />

View File

@@ -138,7 +138,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@@ -168,7 +168,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
@@ -198,7 +198,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@@ -239,7 +239,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -274,7 +274,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
@@ -315,7 +315,7 @@
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<StructMemberAlignment>Default</StructMemberAlignment>
<StructMemberAlignment>4Bytes</StructMemberAlignment>
<BufferSecurityCheck>true</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
@@ -374,9 +374,7 @@
<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" />

View File

@@ -7,18 +7,51 @@
for samples and ideas allowed to make Reed-Solomon coding
more efficient.
* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
* RAR 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.
You may 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.
* RAR encryption includes parts of code from Szymon Stefanek
and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
* 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:
---------------------------------------------------------------------------
Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
All rights reserved.
https://sourceforge.net/projects/slicing-by-8/
LICENSE TERMS
The free distribution and use of this software in both source and binary
form is allowed (with or without changes) provided that:
1. distributions of this source code include the above copyright
notice, this list of conditions and the following disclaimer;
2. distributions in binary form include the above copyright
notice, this list of conditions and the following disclaimer
in the documentation and/or other associated materials;
3. the copyright holder's name is not used to endorse products
built using this software without specific written permission.
ALTERNATIVELY, provided that this notice is retained in full, this product
may be distributed under the terms of the GNU General Public License (GPL),
in which case the provisions of the GPL apply INSTEAD OF those given above.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Source code of this package also as other cryptographic technology
and computing project related links are available on Brian Gladman's
web site: http://www.gladman.me.uk
* RAR uses CRC32 function 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 under BSD License
available at http://www.opensource.org/licenses/bsd-license.html

View File

@@ -1,6 +1,6 @@
static bool IsAnsiEscComment(const wchar *Data,size_t Size);
bool Archive::GetComment(std::wstring &CmtData)
bool Archive::GetComment(Array<wchar> *CmtData)
{
if (!MainComment)
return false;
@@ -11,7 +11,7 @@ bool Archive::GetComment(std::wstring &CmtData)
}
bool Archive::DoGetComment(std::wstring &CmtData)
bool Archive::DoGetComment(Array<wchar> *CmtData)
{
#ifndef SFX_MODULE
uint CmtLength;
@@ -36,12 +36,7 @@ bool Archive::DoGetComment(std::wstring &CmtData)
{
// 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;
return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
}
#ifndef SFX_MODULE
// Old style (RAR 2.9) comment header embedded into the main
@@ -106,21 +101,23 @@ bool Archive::DoGetComment(std::wstring &CmtData)
// 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);
}
CmtData->Alloc(UnpDataSize+1);
memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
CmtData->Alloc(wcslen(CmtData->Addr(0)));
}
}
}
else
{
if (CmtLength==0)
return false;
std::vector<byte> CmtRaw(CmtLength);
int ReadSize=Read(CmtRaw.data(),CmtLength);
Array<byte> CmtRaw(CmtLength);
int ReadSize=Read(&CmtRaw[0],CmtLength);
if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared.
{
CmtLength=ReadSize;
CmtRaw.resize(CmtLength);
CmtRaw.Alloc(CmtLength);
}
if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
@@ -128,41 +125,43 @@ bool Archive::DoGetComment(std::wstring &CmtData)
uiMsg(UIERROR_CMTBROKEN,FileName);
return false;
}
// CmtData.resize(CmtLength+1);
CmtRaw.push_back(0);
CmtData->Alloc(CmtLength+1);
CmtRaw.Push(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());
OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
#endif
CharToWide((const char *)CmtRaw.data(),CmtData);
// CmtData->resize(wcslen(CmtData->data()));
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
CmtData->Alloc(wcslen(CmtData->Addr(0)));
}
#endif
return CmtData.size() > 0;
return CmtData->Size() > 0;
}
bool Archive::ReadCommentData(std::wstring &CmtData)
bool Archive::ReadCommentData(Array<wchar> *CmtData)
{
std::vector<byte> CmtRaw;
Array<byte> CmtRaw;
if (!ReadSubData(&CmtRaw,NULL,false))
return false;
size_t CmtSize=CmtRaw.size();
CmtRaw.push_back(0);
// CmtData->resize(CmtSize+1);
size_t CmtSize=CmtRaw.Size();
CmtRaw.Push(0);
CmtData->Alloc(CmtSize+1);
if (Format==RARFMT50)
UtfToWide((char *)CmtRaw.data(),CmtData);
UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
else
if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
{
CmtData=RawToWide(CmtRaw);
RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
(*CmtData)[CmtSize/2]=0;
}
else
{
CharToWide((const char *)CmtRaw.data(),CmtData);
CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
}
// CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length.
CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
return true;
}
@@ -171,16 +170,15 @@ void Archive::ViewComment()
{
if (Cmd->DisableComment)
return;
std::wstring CmtBuf;
if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments.
Array<wchar> 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);
size_t CmtSize=CmtBuf.Size();
wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
if (ChPtr!=NULL)
CmtSize=ChPtr-&CmtBuf[0];
mprintf(L"\n");
OutComment(&CmtBuf[0],CmtSize);
}
}

View File

@@ -3,15 +3,15 @@
#include "arccmt.cpp"
Archive::Archive(CommandData *InitCmd)
Archive::Archive(RAROptions *InitCmd)
{
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
DummyCmd=(InitCmd==NULL);
Cmd=DummyCmd ? (new CommandData):InitCmd;
Cmd=DummyCmd ? (new RAROptions):InitCmd;
OpenShared=Cmd->OpenShared;
Format=RARFMT_NONE;
Format=RARFMT15;
Solid=false;
Volume=false;
MainComment=false;
@@ -26,21 +26,20 @@ Archive::Archive(CommandData *InitCmd)
FailedHeaderDecryption=false;
BrokenHeader=false;
LastReadBlock=0;
CurHeaderType=HEAD_UNKNOWN;
CurBlockPos=0;
NextBlockPos=0;
RecoveryPercent=-1;
MainHead.Reset();
CryptHead={};
EndArcHead.Reset();
memset(&MainHead,0,sizeof(MainHead));
memset(&CryptHead,0,sizeof(CryptHead));
memset(&EndArcHead,0,sizeof(EndArcHead));
VolNumber=0;
VolWrite=0;
AddingFilesSize=0;
AddingHeadersSize=0;
*FirstVolumeName=0;
Splitting=false;
NewArchive=false;
@@ -69,13 +68,13 @@ void Archive::CheckArc(bool EnableBroken)
// password is incorrect.
if (!FailedHeaderDecryption)
uiMsg(UIERROR_BADARCHIVE,FileName);
ErrHandler.Exit(RARX_BADARC);
ErrHandler.Exit(RARX_FATAL);
}
}
#if !defined(SFX_MODULE)
void Archive::CheckOpen(const std::wstring &Name)
void Archive::CheckOpen(const wchar *Name)
{
TOpen(Name);
CheckArc(false);
@@ -83,7 +82,7 @@ void Archive::CheckOpen(const std::wstring &Name)
#endif
bool Archive::WCheckOpen(const std::wstring &Name)
bool Archive::WCheckOpen(const wchar *Name)
{
if (!WOpen(Name))
return false;
@@ -111,11 +110,9 @@ RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
// 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
@@ -151,9 +148,9 @@ bool Archive::IsArchive(bool EnableBroken)
}
else
{
std::vector<char> Buffer(MAXSFXSIZE);
Array<char> Buffer(MAXSFXSIZE);
long CurPos=(long)Tell();
int ReadSize=Read(Buffer.data(),Buffer.size()-16);
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
for (int I=0;I<ReadSize;I++)
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
{
@@ -234,7 +231,7 @@ bool Archive::IsArchive(bool EnableBroken)
// 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())
if (HeadersLeft && (!SilentOpen || !Encrypted))
{
int64 SavePos=Tell();
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
@@ -268,7 +265,7 @@ bool Archive::IsArchive(bool EnableBroken)
Seek(SavePos,SEEK_SET);
}
if (!Volume || FirstVolume)
FirstVolumeName=FileName;
wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
return true;
}
@@ -304,7 +301,7 @@ uint Archive::FullHeaderSize(size_t Size)
#ifdef USE_QOPEN
bool Archive::Open(const std::wstring &Name,uint Mode)
bool Archive::Open(const wchar *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'.
@@ -339,23 +336,3 @@ int64 Archive::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);
}

View File

@@ -27,27 +27,26 @@ class Archive:public File
{
private:
void UpdateLatestTime(FileHeader *CurBlock);
void ConvertNameCase(std::wstring &Name);
void ConvertNameCase(wchar *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 ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
void RequestArcPassword();
void UnexpEndArcMsg();
void BrokenHeaderMsg();
void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info);
bool DoGetComment(std::wstring &CmtData);
bool ReadCommentData(std::wstring &CmtData);
void UnkEncVerMsg(const wchar *Name,const wchar *Info);
bool DoGetComment(Array<wchar> *CmtData);
bool ReadCommentData(Array<wchar> *CmtData);
#if !defined(RAR_NOCRYPT)
CryptData HeadersCrypt;
#endif
ComprDataIO SubDataIO;
bool DummyCmd;
CommandData *Cmd;
RAROptions *Cmd;
int RecoveryPercent;
RarTime LatestTime;
int LastReadBlock;
@@ -59,19 +58,18 @@ class Archive:public File
bool ProhibitQOpen;
#endif
public:
Archive(CommandData *InitCmd=nullptr);
Archive(RAROptions *InitCmd=NULL);
~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 CheckOpen(const wchar *Name);
bool WCheckOpen(const wchar *Name);
bool GetComment(Array<wchar> *CmtData);
void ViewComment();
void SetLatestTime(RarTime *NewTime);
void SeekToNext();
@@ -81,25 +79,23 @@ class Archive:public File
void VolSubtractHeaderSize(size_t SubSize);
uint FullHeaderSize(size_t Size);
int64 GetStartPos();
void AddSubData(const byte *SrcData,uint64 DataSize,File *SrcFile,
void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
const wchar *Name,uint Flags);
bool ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode);
bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode);
HEADER_TYPE GetHeaderType() {return CurHeaderType;}
CommandData* GetCommandData() {return Cmd;}
RAROptions* GetRAROptions() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
#if 0
void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
#endif
#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;
bool Open(const wchar *Name,uint Mode=FMF_READ);
int Read(void *Data,size_t Size);
void Seek(int64 Offset,int Method);
int64 Tell();
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;
@@ -111,6 +107,7 @@ class Archive:public File
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
UnixOwnersHeader UOHead;
EAHeader EAHead;
StreamHeader StreamHead;
@@ -139,19 +136,12 @@ class Archive:public File
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
wchar FirstVolumeName[NM];
};
#endif

View File

@@ -20,10 +20,10 @@ size_t Archive::ReadHeader()
case RARFMT14:
ReadSize=ReadHeader14();
break;
#endif
case RARFMT15:
ReadSize=ReadHeader15();
break;
#endif
case RARFMT50:
ReadSize=ReadHeader50();
break;
@@ -100,15 +100,9 @@ void Archive::UnexpEndArcMsg()
// If block positions are equal to file size, this is not an error.
// It can happen when we reached the end of older RAR 1.5 archive,
// which did not have the end of archive block.
// We can't replace this check by checking that read size is exactly 0
// in the beginning of file header, because in this case the read position
// still can be beyond the end of archive.
if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{
uiMsg(UIERROR_UNEXPEOF,FileName);
if (CurHeaderType!=HEAD_FILE && CurHeaderType!=HEAD_UNKNOWN)
uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName);
ErrHandler.SetErrorCode(RARX_WARNING);
}
}
@@ -122,10 +116,10 @@ void Archive::BrokenHeaderMsg()
}
void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info)
void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
{
uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
ErrHandler.SetErrorCode(RARX_FATAL);
ErrHandler.SetErrorCode(RARX_WARNING);
}
@@ -140,7 +134,6 @@ inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
}
#ifndef SFX_MODULE
size_t Archive::ReadHeader15()
{
RawRead Raw(this);
@@ -149,10 +142,10 @@ size_t Archive::ReadHeader15()
if (Decrypt)
{
#ifdef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll.
#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
return 0;
#else
RequestArcPassword(NULL);
RequestArcPassword();
byte Salt[SIZE_SALT30];
if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
@@ -226,7 +219,7 @@ size_t Archive::ReadHeader15()
{
case HEAD_MAIN:
MainHead.Reset();
MainHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&MainHead=ShortBlock;
MainHead.HighPosAV=Raw.Get2();
MainHead.PosAV=Raw.Get4();
@@ -252,17 +245,13 @@ size_t Archive::ReadHeader15()
FileHeader *hd=FileBlock ? &FileHead:&SubHead;
hd->Reset();
hd->SetBaseBlock(ShortBlock);
*(BaseBlock *)hd=ShortBlock;
hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
// RAR versions earlier than 2.0 do not set the solid flag
// in file header. They use only a global solid archive flag.
hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
@@ -311,7 +300,7 @@ size_t Archive::ReadHeader15()
if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
{
hd->RedirType=FSREDIR_UNIXSYMLINK;
hd->RedirName.clear();
*hd->RedirName=0;
}
hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
@@ -338,26 +327,27 @@ size_t Archive::ReadHeader15()
if (hd->UnknownUnpSize)
hd->UnpSize=INT64NDF;
size_t ReadNameSize=Min(NameSize,MAXPATHSIZE);
std::string FileName(ReadNameSize,0);
Raw.GetB((byte *)&FileName[0],ReadNameSize);
char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
if (FileBlock)
{
hd->FileName.clear();
*hd->FileName=0;
if ((hd->Flags & LHD_UNICODE)!=0)
{
EncodeFileName NameCoder;
size_t Length=strlen(FileName.data());
size_t Length=strlen(FileName);
Length++;
if (ReadNameSize>Length)
NameCoder.Decode(FileName.data(),ReadNameSize,
(byte *)&FileName[Length],
ReadNameSize-Length,hd->FileName);
NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
ReadNameSize-Length,hd->FileName,
ASIZE(hd->FileName));
}
if (hd->FileName.empty())
ArcCharToWide(FileName.data(),hd->FileName,ACTW_OEM);
if (*hd->FileName==0)
ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
#ifndef SFX_MODULE
ConvertNameCase(hd->FileName);
@@ -366,7 +356,7 @@ size_t Archive::ReadHeader15()
}
else
{
CharToWide(FileName.data(),hd->FileName);
CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
// Calculate the size of optional data.
int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
@@ -377,15 +367,14 @@ size_t Archive::ReadHeader15()
{
// Here we read optional additional fields for subheaders.
// They are stored after the file name and before salt.
hd->SubData.resize(DataSize);
Raw.GetB(hd->SubData.data(),DataSize);
hd->SubData.Alloc(DataSize);
Raw.GetB(&hd->SubData[0],DataSize);
}
if (hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true;
}
if ((hd->Flags & LHD_SALT)!=0)
Raw.GetB(hd->Salt,SIZE_SALT30);
hd->mtime.SetDos(FileTime);
@@ -428,7 +417,7 @@ size_t Archive::ReadHeader15()
NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
bool CRCProcessedOnly=hd->CommentInHeader;
uint HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
if (hd->HeadCRC!=HeaderCRC)
{
BrokenHeader=true;
@@ -445,7 +434,7 @@ size_t Archive::ReadHeader15()
}
break;
case HEAD_ENDARC:
EndArcHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&EndArcHead=ShortBlock;
EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
@@ -457,14 +446,14 @@ size_t Archive::ReadHeader15()
break;
#ifndef SFX_MODULE
case HEAD3_CMT:
CommHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&CommHead=ShortBlock;
CommHead.UnpSize=Raw.Get2();
CommHead.UnpVer=Raw.Get1();
CommHead.Method=Raw.Get1();
CommHead.CommCRC=Raw.Get2();
break;
case HEAD3_PROTECT:
ProtectHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&ProtectHead=ShortBlock;
ProtectHead.DataSize=Raw.Get4();
ProtectHead.Version=Raw.Get1();
ProtectHead.RecSectors=Raw.Get2();
@@ -473,13 +462,26 @@ size_t Archive::ReadHeader15()
NextBlockPos+=ProtectHead.DataSize;
break;
case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
SubBlockHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&SubBlockHead=ShortBlock;
SubBlockHead.DataSize=Raw.Get4();
NextBlockPos+=SubBlockHead.DataSize;
SubBlockHead.SubType=Raw.Get2();
SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
case UO_HEAD:
*(SubBlockHeader *)&UOHead=SubBlockHead;
UOHead.OwnerNameSize=Raw.Get2();
UOHead.GroupNameSize=Raw.Get2();
if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
UOHead.OwnerName[UOHead.OwnerNameSize]=0;
UOHead.GroupName[UOHead.GroupNameSize]=0;
break;
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
EAHead.UnpSize=Raw.Get4();
@@ -494,13 +496,10 @@ size_t Archive::ReadHeader15()
StreamHead.Method=Raw.Get1();
StreamHead.StreamCRC=Raw.Get4();
StreamHead.StreamNameSize=Raw.Get2();
const size_t MaxStreamName20=260; // Maximum allowed stream name in RAR 2.x format.
if (StreamHead.StreamNameSize>MaxStreamName20)
StreamHead.StreamNameSize=MaxStreamName20;
StreamHead.StreamName.resize(StreamHead.StreamNameSize);
Raw.GetB(&StreamHead.StreamName[0],StreamHead.StreamNameSize);
if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
StreamHead.StreamName[StreamHead.StreamNameSize]=0;
break;
}
break;
@@ -510,16 +509,12 @@ size_t Archive::ReadHeader15()
NextBlockPos+=Raw.Get4();
break;
}
uint HeaderCRC=Raw.GetCRC15(false);
ushort HeaderCRC=Raw.GetCRC15(false);
// Old AV header does not have header CRC properly set.
// Old Unix owners header didn't include string fields into header size,
// but included them into CRC, so it couldn't be verified with generic
// approach here.
if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
ShortBlock.HeaderType!=HEAD3_AV &&
(ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD))
ShortBlock.HeaderType!=HEAD3_AV)
{
bool Recovered=false;
if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
@@ -549,7 +544,6 @@ size_t Archive::ReadHeader15()
return Raw.Size();
}
#endif // #ifndef SFX_MODULE
size_t Archive::ReadHeader50()
@@ -564,13 +558,6 @@ size_t Archive::ReadHeader50()
return 0;
#else
if (Cmd->SkipEncrypted)
{
uiMsg(UIMSG_SKIPENCARC,FileName);
FailedHeaderDecryption=true; // Suppress error messages and quit quietly.
return 0;
}
byte HeadersInitV[SIZE_INITV];
if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
{
@@ -583,21 +570,15 @@ size_t Archive::ReadHeader50()
// in -p<pwd> to not stop batch processing for encrypted archives.
bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
RarCheckPassword CheckPwd;
if (CryptHead.UsePswCheck && !BrokenHeader)
CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck);
while (true) // Repeat the password prompt for wrong passwords.
{
RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL);
RequestArcPassword();
byte PswCheck[SIZE_PSWCHECK];
bool EncSet=HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
// Verify password validity. If header is damaged, we cannot rely on
// password check value, because it can be damaged too.
if (EncSet && CryptHead.UsePswCheck && !BrokenHeader &&
memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
{
byte PswCheck[SIZE_PSWCHECK];
HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
// Verify password validity.
if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
{
if (GlobalPassword) // For -p<pwd> or Ctrl+P.
{
// This message is used by Android GUI to reset cached passwords.
@@ -654,7 +635,7 @@ size_t Archive::ReadHeader50()
}
int SizeToRead=int(BlockSize);
SizeToRead-=int(FirstReadSize-SizeBytes-4); // Adjust overread size bytes if any.
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
uint HeaderSize=4+SizeBytes+(uint)BlockSize;
if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
@@ -719,12 +700,13 @@ size_t Archive::ReadHeader50()
{
case HEAD_CRYPT:
{
CryptHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&CryptHead=ShortBlock;
uint CryptVersion=(uint)Raw.GetV();
if (CryptVersion>CRYPT_VERSION)
{
UnkEncVerMsg(FileName,L"h" + std::to_wstring(CryptVersion));
FailedHeaderDecryption=true;
wchar Info[20];
swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
UnkEncVerMsg(FileName,Info);
return 0;
}
uint EncFlags=(uint)Raw.GetV();
@@ -732,8 +714,9 @@ size_t Archive::ReadHeader50()
CryptHead.Lg2Count=Raw.Get1();
if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
{
UnkEncVerMsg(FileName,L"hc" + std::to_wstring(CryptHead.Lg2Count));
FailedHeaderDecryption=true;
wchar Info[20];
swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
UnkEncVerMsg(FileName,Info);
return 0;
}
@@ -745,15 +728,14 @@ size_t Archive::ReadHeader50()
byte csum[SIZE_PSWCHECK_CSUM];
Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
// Exclude this code for rarext.dll, Setup.SFX and unrar_nocrypt.dll linked
// without sha256. But still set Encrypted=true for rarext.dll here,
// so it can recognize encrypted header archives in archive properties.
#ifndef RAR_NOCRYPT
sha256_context ctx;
sha256_init(&ctx);
sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
byte Digest[SHA256_DIGEST_SIZE];
sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest);
sha256_done(&ctx, Digest);
CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
#endif
}
Encrypted=true;
}
@@ -761,7 +743,7 @@ size_t Archive::ReadHeader50()
case HEAD_MAIN:
{
MainHead.Reset();
MainHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&MainHead=ShortBlock;
uint ArcFlags=(uint)Raw.GetV();
Volume=(ArcFlags & MHFL_VOLUME)!=0;
@@ -803,7 +785,7 @@ size_t Archive::ReadHeader50()
case HEAD_SERVICE:
{
FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
hd->Reset(); // Clear hash, time fields and other stuff like flags.
hd->Reset();
*(BaseBlock *)hd=ShortBlock;
bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
@@ -839,14 +821,9 @@ size_t Archive::ReadHeader50()
// we may need to use the compression algorithm 15 in the future,
// but it was already used in RAR 1.5 and Unpack needs to distinguish
// them.
uint UnpVer=(CompInfo & 0x3f);
if (UnpVer==0)
hd->UnpVer=VER_PACK5;
else
if (UnpVer==1)
hd->UnpVer=VER_PACK7;
else
hd->UnpVer=VER_UNKNOWN;
hd->UnpVer=(CompInfo & 0x3f) + 50;
if (hd->UnpVer!=50) // Only 5.0 compression is known now.
hd->UnpVer=VER_UNKNOWN;
hd->HostOS=(byte)Raw.GetV();
size_t NameSize=(size_t)Raw.GetV();
@@ -864,29 +841,16 @@ size_t Archive::ReadHeader50()
hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
if (hd->Dir || UnpVer>1)
hd->WinSize=0;
else
{
hd->WinSize=0x20000ULL<<((CompInfo>>10)&(UnpVer==0 ? 0x0f:0x1f));
if (UnpVer==1)
{
hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f);
hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
// RAR7 header with RAR5 compression. Needed to append RAR7 files
// to RAR5 solid stream if new dictionary is larger than existing.
if ((CompInfo & FCI_RAR5_COMPAT)!=0)
hd->UnpVer=VER_PACK5;
if (hd->WinSize>UNPACK_MAX_DICT)
hd->UnpVer=VER_UNKNOWN;
}
}
hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
size_t ReadNameSize=Min(NameSize,MAXPATHSIZE);
std::string FileName(ReadNameSize,0);
Raw.GetB((byte *)&FileName[0],ReadNameSize);
char FileName[NM*4];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
UtfToWide(FileName.data(),hd->FileName);
UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
// Should do it before converting names, because extra fields can
// affect name processing, like in case of NTFS streams.
@@ -904,25 +868,28 @@ size_t Archive::ReadHeader50()
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
MainComment=true;
#if 0
// For RAR5 format we read the user specified recovery percent here.
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.size()>0)
// It would be useful to do it for shell extension too, so we display
// the correct recovery record size in archive properties. But then
// we would need to include the entire recovery record processing
// code to shell extension, which is not done now.
if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
{
// It is stored as a single byte up to RAR 6.02 and as vint since
// 6.10, where we extended the maximum RR size from 99% to 1000%.
RawRead RawPercent;
RawPercent.Read(hd->SubData.data(),hd->SubData.size());
RecoveryPercent=(int)RawPercent.GetV();
RecoveryPercent=hd->SubData[0];
RSBlockHeader Header;
GetRRInfo(this,&Header);
RecoverySize=Header.RecSectionSize*Header.RecCount;
}
#endif
if (BadCRC) // Add the file name to broken header message displayed above.
uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
}
break;
case HEAD_ENDARC:
{
EndArcHead.SetBaseBlock(ShortBlock);
*(BaseBlock *)&EndArcHead=ShortBlock;
uint ArcFlags=(uint)Raw.GetV();
EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
EndArcHead.StoreVolNumber=false;
@@ -937,7 +904,7 @@ size_t Archive::ReadHeader50()
#if !defined(RAR_NOCRYPT)
void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
void Archive::RequestArcPassword()
{
if (!Cmd->Password.IsSet())
{
@@ -954,7 +921,7 @@ void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
*PasswordA=0;
if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
*PasswordA=0;
CharToWide(PasswordA,PasswordW,ASIZE(PasswordW));
GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
cleandata(PasswordA,sizeof(PasswordA));
}
Cmd->Password.Set(PasswordW);
@@ -967,7 +934,7 @@ void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
ErrHandler.Exit(RARX_USERBREAK);
}
#else
if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd))
if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
{
Close();
uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
@@ -980,7 +947,7 @@ void Archive::RequestArcPassword(RarCheckPassword *CheckPwd)
#endif
void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
{
// Read extra data from the end of block skipping any fields before it.
size_t ExtraStart=Raw->Size()-ExtraSize;
@@ -1003,52 +970,22 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
if (bb->HeaderType==HEAD_MAIN)
{
MainHeader *hd=(MainHeader *)bb;
switch(FieldType)
if (FieldType==MHEXTRA_LOCATOR)
{
case MHEXTRA_LOCATOR:
{
hd->Locator=true;
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->QOpenOffset=Offset+CurBlockPos;
}
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->RROffset=Offset+CurBlockPos;
}
}
break;
case MHEXTRA_METADATA:
{
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_METADATA_NAME)!=0)
{
uint64 NameSize=Raw->GetV();
if (NameSize>0 && NameSize<MAXPATHSIZE) // Prevent excessive allocation.
{
std::string NameU((size_t)NameSize,0); // UTF-8 name.
Raw->GetB(&NameU[0],(size_t)NameSize);
// If starts from 0, the name was longer than reserved space
// when saving this extra field.
if (NameU[0]!=0)
UtfToWide(&NameU[0],hd->OrigName);
}
}
if ((Flags & MHEXTRA_METADATA_CTIME)!=0)
if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0)
if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0)
hd->OrigTime.SetUnixNS(Raw->Get8());
else
hd->OrigTime.SetUnix((time_t)Raw->Get4());
else
hd->OrigTime.SetWin(Raw->Get8());
}
break;
hd->Locator=true;
uint Flags=(uint)Raw->GetV();
if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->QOpenOffset=Offset+CurBlockPos;
}
if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
{
uint64 Offset=Raw->GetV();
if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
hd->RROffset=Offset+CurBlockPos;
}
}
}
@@ -1057,64 +994,64 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
FileHeader *hd=(FileHeader *)bb;
switch(FieldType)
{
#ifndef RAR_NOCRYPT // Except rarext.dll, Setup.SFX and unrar_nocrypt.dll.
case FHEXTRA_CRYPT:
{
FileHeader *hd=(FileHeader *)bb;
uint EncVersion=(uint)Raw->GetV();
if (EncVersion>CRYPT_VERSION)
if (EncVersion > CRYPT_VERSION)
{
UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion));
hd->CryptMethod=CRYPT_UNKNOWN;
wchar Info[20];
swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
UnkEncVerMsg(hd->FileName,Info);
}
else
{
uint Flags=(uint)Raw->GetV();
hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
hd->Lg2Count=Raw->Get1();
if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
{
UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count));
hd->CryptMethod=CRYPT_UNKNOWN;
wchar Info[20];
swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
UnkEncVerMsg(hd->FileName,Info);
}
else
Raw->GetB(hd->Salt,SIZE_SALT50);
Raw->GetB(hd->InitV,SIZE_INITV);
if (hd->UsePswCheck)
{
hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
Raw->GetB(hd->Salt,SIZE_SALT50);
Raw->GetB(hd->InitV,SIZE_INITV);
if (hd->UsePswCheck)
{
Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
// It is important to know if password check data is valid.
// If it is damaged and header CRC32 fails to detect it,
// archiver would refuse to decompress a possibly valid file.
// Since we want to be sure distinguishing a wrong password
// or corrupt file data, we use 64-bit password check data
// and to control its validity we use 32 bits of password
// check data SHA-256 additionally to 32-bit header CRC32.
byte csum[SIZE_PSWCHECK_CSUM];
Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
// It is important to know if password check data is valid.
// If it is damaged and header CRC32 fails to detect it,
// archiver would refuse to decompress a possibly valid file.
// Since we want to be sure distinguishing a wrong password
// or corrupt file data, we use 64-bit password check data
// and to control its validity we use 32 bits of password
// check data SHA-256 additionally to 32-bit header CRC32.
byte csum[SIZE_PSWCHECK_CSUM];
Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
sha256_context ctx;
sha256_init(&ctx);
sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
byte Digest[SHA256_DIGEST_SIZE];
sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest);
byte Digest[SHA256_DIGEST_SIZE];
sha256_done(&ctx, Digest);
hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
// RAR 5.21 and earlier set PswCheck field in service records to 0
// even if UsePswCheck was present.
if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
hd->UsePswCheck=0;
}
hd->SaltSet=true;
hd->CryptMethod=CRYPT_RAR50;
hd->Encrypted=true;
// RAR 5.21 and earlier set PswCheck field in service records to 0
// even if UsePswCheck was present.
if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
hd->UsePswCheck=0;
}
hd->SaltSet=true;
hd->CryptMethod=CRYPT_RAR50;
hd->Encrypted=true;
}
}
break;
#endif
case FHEXTRA_HASH:
{
FileHeader *hd=(FileHeader *)bb;
@@ -1166,27 +1103,31 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
if (Version!=0)
{
hd->Version=true;
hd->FileName += L';' + std::to_wstring(Version);
wchar VerText[20];
swprintf(VerText,ASIZE(VerText),L";%u",Version);
wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
}
}
break;
case FHEXTRA_REDIR:
{
FILE_SYSTEM_REDIRECT RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
uint Flags=(uint)Raw->GetV();
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
size_t NameSize=(size_t)Raw->GetV();
if (NameSize>0 && NameSize<MAXPATHSIZE)
char UtfName[NM*4];
*UtfName=0;
if (NameSize<ASIZE(UtfName)-1)
{
std::string UtfName(NameSize,0);
hd->RedirType=RedirType;
hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
Raw->GetB(&UtfName[0],NameSize);
UtfToWide(&UtfName[0],hd->RedirName);
#ifdef _WIN_ALL
UnixSlashToDos(hd->RedirName,hd->RedirName);
#endif
Raw->GetB(UtfName,NameSize);
UtfName[NameSize]=0;
}
#ifdef _WIN_ALL
UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
#endif
UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
}
break;
case FHEXTRA_UOWNER:
@@ -1243,8 +1184,8 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb)
// We cannot allocate too much memory here, because above
// we check FieldSize againt Raw size and we control that Raw size
// is sensible when reading headers.
hd->SubData.resize((size_t)FieldSize);
Raw->GetB(hd->SubData.data(),(size_t)FieldSize);
hd->SubData.Alloc((size_t)FieldSize);
Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
}
break;
}
@@ -1267,7 +1208,7 @@ size_t Archive::ReadHeader14()
Raw.GetB(Mark,4);
uint HeadSize=Raw.Get2();
if (HeadSize<7)
return 0;
return false;
byte Flags=Raw.Get1();
NextBlockPos=CurBlockPos+HeadSize;
CurHeaderType=HEAD_MAIN;
@@ -1290,7 +1231,7 @@ size_t Archive::ReadHeader14()
FileHead.FileHash.CRC32=Raw.Get2();
FileHead.HeadSize=Raw.Get2();
if (FileHead.HeadSize<21)
return 0;
return false;
uint FileTime=Raw.Get4();
FileHead.FileAttr=Raw.Get1();
FileHead.Flags=Raw.Get1()|LONG_BLOCK;
@@ -1314,13 +1255,12 @@ size_t Archive::ReadHeader14()
Raw.Read(NameSize);
// RAR 1.4 name size is stored in a single byte field and it can't
// exceed 255, so additional checks are not needed.
std::string FileName(NameSize,0);
Raw.GetB((byte *)&FileName[0],NameSize);
std::string NameA;
OemToExt(FileName,NameA);
CharToWide(NameA,FileHead.FileName);
char FileName[NM];
size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
Raw.GetB((byte *)FileName,ReadNameSize);
FileName[ReadNameSize]=0;
IntToExt(FileName,FileName,ASIZE(FileName));
CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
ConvertNameCase(FileHead.FileName);
ConvertFileHeader(&FileHead);
@@ -1334,7 +1274,7 @@ size_t Archive::ReadHeader14()
#ifndef SFX_MODULE
void Archive::ConvertNameCase(std::wstring &Name)
void Archive::ConvertNameCase(wchar *Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
wcsupper(Name);
@@ -1352,7 +1292,7 @@ bool Archive::IsArcDir()
void Archive::ConvertAttributes()
{
#ifdef _WIN_ALL
#if defined(_WIN_ALL) || defined(_EMX)
if (FileHead.HSType!=HSYS_WINDOWS)
FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
#endif
@@ -1417,23 +1357,19 @@ void Archive::ConvertAttributes()
void Archive::ConvertFileHeader(FileHeader *hd)
{
/*
if (hd->HSType==HSYS_UNKNOWN)
if (hd->Dir)
hd->FileAttr=0x10;
else
hd->FileAttr=0x20;
*/
#ifdef _WIN_ALL
if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
ConvertToPrecomposed(hd->FileName);
ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
#endif
for (uint I=0;I<hd->FileName.size();I++)
for (wchar *s=hd->FileName;*s!=0;s++)
{
wchar *s=&hd->FileName[I];
#ifdef _UNIX
// Backslash is the invalid character for Windows file headers,
// but it can present in Unix file names extracted in Unix.
@@ -1441,7 +1377,7 @@ void Archive::ConvertFileHeader(FileHeader *hd)
*s='_';
#endif
#ifdef _WIN_ALL
#if defined(_WIN_ALL) || defined(_EMX)
// RAR 5.0 archives do not use '\' as path separator, so if we see it,
// it means that it is a part of Unix file name, which we cannot
// extract in Windows.
@@ -1466,9 +1402,6 @@ void Archive::ConvertFileHeader(FileHeader *hd)
if (*s=='/' || *s=='\\' && Format!=RARFMT50)
*s=CPATHDIVIDER;
}
// Zeroes inside might be possible in broken Unicode names decoded with EncodeFileName::Decode.
TruncateAtZero(hd->FileName); // Ensure there are no zeroes inside of string.
}
@@ -1483,7 +1416,7 @@ int64 Archive::GetStartPos()
}
bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode)
bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
if (BrokenHeader)
{
@@ -1491,7 +1424,7 @@ bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMod
ErrHandler.SetErrorCode(RARX_CRC);
return false;
}
if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK7:VER_UNPACK))
if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
{
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
@@ -1508,9 +1441,7 @@ bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMod
{
if (SubHead.UnpSize>0x1000000)
{
// Prevent the excessive allocation. When reading to memory, normally
// this function operates with reasonably small blocks, such as
// the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream.
// So huge allocation must never happen in valid archives.
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
}
@@ -1518,7 +1449,7 @@ bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMod
SubDataIO.SetTestMode(true);
else
{
UnpData->resize((size_t)SubHead.UnpSize);
UnpData->Alloc((size_t)SubHead.UnpSize);
SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
}
}
@@ -1547,7 +1478,7 @@ bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMod
uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
ErrHandler.SetErrorCode(RARX_CRC);
if (UnpData!=NULL)
UnpData->clear();
UnpData->Reset();
return false;
}
return true;

191
unrar/array.hpp Normal file
View File

@@ -0,0 +1,191 @@
#ifndef _RAR_ARRAY_
#define _RAR_ARRAY_
extern ErrorHandler ErrHandler;
template <class T> class Array
{
private:
T *Buffer;
size_t BufSize;
size_t AllocSize;
size_t MaxSize;
bool Secure; // Clean memory if true.
public:
Array();
Array(size_t Size);
Array(const Array &Src); // Copy constructor.
~Array();
inline void CleanData();
inline T& operator [](size_t Item) const;
inline T* operator + (size_t Pos);
inline size_t Size(); // Returns the size in items, not in bytes.
void Add(size_t Items);
void Alloc(size_t Items);
void Reset();
void SoftReset();
void operator = (Array<T> &Src);
void Push(T Item);
void Append(T *Item,size_t Count);
T* Addr(size_t Item) {return Buffer+Item;}
void SetMaxSize(size_t Size) {MaxSize=Size;}
T* Begin() {return Buffer;}
T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
void SetSecure() {Secure=true;}
};
template <class T> void Array<T>::CleanData()
{
Buffer=NULL;
BufSize=0;
AllocSize=0;
MaxSize=0;
Secure=false;
}
template <class T> Array<T>::Array()
{
CleanData();
}
template <class T> Array<T>::Array(size_t Size)
{
CleanData();
Add(Size);
}
// Copy constructor in case we need to pass an object as value.
template <class T> Array<T>::Array(const Array &Src)
{
CleanData();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
}
template <class T> Array<T>::~Array()
{
if (Buffer!=NULL)
{
if (Secure)
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
template <class T> inline T& Array<T>::operator [](size_t Item) const
{
return Buffer[Item];
}
template <class T> inline T* Array<T>::operator +(size_t Pos)
{
return Buffer+Pos;
}
template <class T> inline size_t Array<T>::Size()
{
return BufSize;
}
template <class T> void Array<T>::Add(size_t Items)
{
BufSize+=Items;
if (BufSize>AllocSize)
{
if (MaxSize!=0 && BufSize>MaxSize)
{
ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
ErrHandler.MemoryError();
}
size_t Suggested=AllocSize+AllocSize/4+32;
size_t NewSize=Max(BufSize,Suggested);
T *NewBuffer;
if (Secure)
{
NewBuffer=(T *)malloc(NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
if (Buffer!=NULL)
{
memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
cleandata(Buffer,AllocSize*sizeof(T));
free(Buffer);
}
}
else
{
NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
if (NewBuffer==NULL)
ErrHandler.MemoryError();
}
Buffer=NewBuffer;
AllocSize=NewSize;
}
}
template <class T> void Array<T>::Alloc(size_t Items)
{
if (Items>AllocSize)
Add(Items-BufSize);
else
BufSize=Items;
}
template <class T> void Array<T>::Reset()
{
if (Buffer!=NULL)
{
free(Buffer);
Buffer=NULL;
}
BufSize=0;
AllocSize=0;
}
// Reset buffer size, but preserve already allocated memory if any,
// so we can reuse it without wasting time to allocation.
template <class T> void Array<T>::SoftReset()
{
BufSize=0;
}
template <class T> void Array<T>::operator =(Array<T> &Src)
{
Reset();
Alloc(Src.BufSize);
if (Src.BufSize!=0)
memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
}
template <class T> void Array<T>::Push(T Item)
{
Add(1);
(*this)[Size()-1]=Item;
}
template <class T> void Array<T>::Append(T *Items,size_t Count)
{
size_t CurSize=Size();
Add(Count);
memcpy(Buffer+CurSize,Items,Count*sizeof(T));
}
#endif

View File

@@ -2,20 +2,6 @@
#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
@@ -32,6 +18,20 @@ static const uint32 blake2s_IV[8] =
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};
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 } ,
};
static inline void blake2s_set_lastnode( blake2s_state *S )
{
S->f[1] = ~0U;
@@ -134,7 +134,11 @@ void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
#ifdef USE_SSE
#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
if (_SSE_Version>=SSE_SSE2)
#else
if (_SSE_Version>=SSE_SSSE3)
#endif
blake2s_compress_sse( S, S->buf );
else
blake2s_compress( S, S->buf ); // Compress

View File

@@ -5,9 +5,12 @@
#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;
enum blake2s_constant
{
BLAKE2S_BLOCKBYTES = 64,
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.
@@ -17,15 +20,10 @@ constexpr size_t BLAKE2S_OUTBYTES = 32;
// '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;
enum { 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;
enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];

View File

@@ -1,14 +1,15 @@
// Based on public domain code written in 2012 by Samuel Neves
extern const byte blake2s_sigma[10][16];
// Initialization vector.
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
// Constants for cyclic rotation.
#ifdef _WIN_64
// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
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
@@ -23,18 +24,28 @@ static void blake2s_init_sse()
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
#ifdef _WIN_64
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 );
#endif
}
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
#ifdef _WIN_32
// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
// to not use _mm_shuffle_epi8 here.
#define mm_rotr_epi32(r, c) ( \
_mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
#else
#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 )) )
#endif
#define G1(row1,row2,row3,row4,buf) \
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
@@ -62,6 +73,14 @@ static void blake2s_init_sse()
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
#ifdef _WIN_64
// MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
// from stack operations, which are slower than this code.
#define _mm_set_epi32(i3,i2,i1,i0) \
_mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
_mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
#endif
// 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
@@ -82,9 +101,6 @@ static void blake2s_init_sse()
}
#ifdef __GNUC__
__attribute__((target("ssse3")))
#endif
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
{
__m128i row[4];

View File

@@ -20,7 +20,7 @@ void blake2sp_init( blake2sp_state *S )
blake2s_init_param( &S->R, 0, 1 ); // Init root.
for( uint32 i = 0; i < PARALLELISM_DEGREE; ++i )
for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
S->R.last_node = 1;
@@ -49,8 +49,6 @@ void Blake2ThreadData::Update()
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;

View File

@@ -13,18 +13,8 @@ void CommandData::Init()
{
RAROptions::Init();
Command.clear();
ArcName.clear();
ExtrPath.clear();
TempPath.clear();
SFXModule.clear();
CommentFile.clear();
ArcPath.clear();
ExclArcPath.clear();
LogName.clear();
EmailTo.clear();
UseStdin.clear();
*Command=0;
*ArcName=0;
FileLists=false;
NoMoreSwitches=false;
@@ -36,52 +26,60 @@ void CommandData::Init()
FileArgs.Reset();
ExclArgs.Reset();
InclArgs.Reset();
ArcNames.Reset();
StoreArgs.Reset();
#ifdef PROPAGATE_MOTW
MotwList.Reset();
#endif
Password.Clean();
NextVolSizes.clear();
#ifdef RARDLL
DllDestName.clear();
#endif
ArcNames.Reset();
NextVolSizes.Reset();
}
// Return the pointer to next position in the string and store dynamically
// allocated command line parameter in Par.
static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
{
const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
if (NextCmd==NULL)
return NULL;
size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
*Par=(wchar *)malloc(ParSize*sizeof(wchar));
if (*Par==NULL)
return NULL;
return GetCmdParam(CmdLine,*Par,ParSize);
}
#if !defined(SFX_MODULE)
void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
{
Command.clear();
*Command=0;
NoMoreSwitches=false;
#ifdef CUSTOM_CMDLINE_PARSER
// In Windows we may prefer to implement our own command line parser
// to avoid replacing \" by " in standard parser. Such replacing corrupts
// destination paths like "dest path\" in extraction commands.
std::wstring CmdLine=GetCommandLine();
std::wstring Param;
std::wstring::size_type Pos=0;
const wchar *CmdLine=GetCommandLine();
wchar *Par;
for (bool FirstParam=true;;FirstParam=false)
{
if (!GetCmdParam(CmdLine,Pos,Param))
if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
break;
if (!FirstParam) // First parameter is the executable name.
if (Preprocess)
PreprocessArg(Param.c_str());
PreprocessArg(Par);
else
ParseArg(Param.c_str());
ParseArg(Par);
free(Par);
}
#else
Array<wchar> Arg;
for (int I=1;I<argc;I++)
{
std::wstring Arg;
CharToWide(argv[I],Arg);
Arg.Alloc(strlen(argv[I])+1);
CharToWide(argv[I],&Arg[0],Arg.Size());
if (Preprocess)
PreprocessArg(Arg.c_str());
PreprocessArg(&Arg[0]);
else
ParseArg(Arg.c_str());
ParseArg(&Arg[0]);
}
#endif
if (!Preprocess)
@@ -91,7 +89,7 @@ void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
#if !defined(SFX_MODULE)
void CommandData::ParseArg(const wchar *Arg)
void CommandData::ParseArg(wchar *Arg)
{
if (IsSwitch(*Arg) && !NoMoreSwitches)
if (Arg[1]=='-' && Arg[2]==0)
@@ -99,52 +97,34 @@ void CommandData::ParseArg(const wchar *Arg)
else
ProcessSwitch(Arg+1);
else
if (Command.empty())
if (*Command==0)
{
Command=Arg;
wcsncpyz(Command,Arg,ASIZE(Command));
Command[0]=toupperw(Command[0]);
*Command=toupperw(*Command);
// 'I' and 'S' commands can contain case sensitive strings after
// the first character, so we must not modify their case.
// 'S' can contain SFX name, which case is important in Unix.
if (Command[0]!='I' && Command[0]!='S')
if (*Command!='I' && *Command!='S')
wcsupper(Command);
if (Command[0]=='P') // Enforce -idq for print command.
{
MsgStream=MSG_ERRONLY;
SetConsoleMsgStream(MSG_ERRONLY);
}
}
else
if (ArcName.empty())
ArcName=Arg;
if (*ArcName==0)
wcsncpyz(ArcName,Arg,ASIZE(ArcName));
else
{
// Check if last character is the path separator.
size_t Length=wcslen(Arg);
wchar EndChar=Length==0 ? 0:Arg[Length-1];
// Check if trailing path separator like path\ is present.
bool FolderArg=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
// 2024.01.05: We were asked to support exotic d:. and d:.. paths.
if (IsDriveLetter(Arg) && Arg[2]=='.' && (Arg[3]==0 || Arg[3]=='.' && Arg[4]==0))
FolderArg=true;
// 2024.01.06: FindFile::FastFind check below fails in Windows 10 if
// "." or ".." points at disk root. So we enforce it for "." and ".."
// optionally preceded with some path like "..\..".
size_t L=Length;
if (L>0 && Arg[L-1]=='.' && (L==1 || L>=2 && (IsPathDiv(Arg[L-2]) ||
Arg[L-2]=='.' && (L==2 || L>=3 && IsPathDiv(Arg[L-3])))))
FolderArg=true;
wchar CmdChar=toupperw(Command[0]);
wchar CmdChar=toupperw(*Command);
bool Add=wcschr(L"AFUM",CmdChar)!=NULL;
bool Extract=CmdChar=='X' || CmdChar=='E';
bool Repair=CmdChar=='R' && Command[1]==0;
if (FolderArg && !Add)
ExtrPath=Arg;
if (EndSeparator && !Add)
wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
else
if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS))
FileArgs.AddString(Arg);
@@ -161,10 +141,10 @@ void CommandData::ParseArg(const wchar *Arg)
}
else // We use 'destpath\' when extracting and reparing.
if (Found && FileData.IsDir && (Extract || Repair) && ExtrPath.empty())
if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
{
ExtrPath=Arg;
AddEndSlash(ExtrPath);
wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
AddEndSlash(ExtrPath,ASIZE(ExtrPath));
}
else
FileArgs.AddString(Arg);
@@ -192,12 +172,12 @@ void CommandData::ParseDone()
#if !defined(SFX_MODULE)
void CommandData::ParseEnvVar()
{
char *EnvVar=getenv("RARINISWITCHES");
if (EnvVar!=NULL)
char *EnvStr=getenv("RAR");
if (EnvStr!=NULL)
{
std::wstring EnvStr;
CharToWide(EnvVar,EnvStr);
ProcessSwitchesString(EnvStr);
Array<wchar> EnvStrW(strlen(EnvStr)+1);
CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
ProcessSwitchesString(&EnvStrW[0]);
}
}
#endif
@@ -206,7 +186,7 @@ void CommandData::ParseEnvVar()
#if !defined(SFX_MODULE)
// Preprocess those parameters, which must be processed before the rest of
// command line.
// command line. Return 'false' to stop further processing.
void CommandData::PreprocessArg(const wchar *Arg)
{
if (IsSwitch(Arg[0]) && !NoMoreSwitches)
@@ -215,7 +195,7 @@ void CommandData::PreprocessArg(const wchar *Arg)
if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
NoMoreSwitches=true;
if (wcsicomp(Arg,L"cfg-")==0)
ProcessSwitch(Arg);
ConfigDisabled=true;
if (wcsnicomp(Arg,L"ilog",4)==0)
{
// Ensure that correct log file name is already set
@@ -227,13 +207,13 @@ void CommandData::PreprocessArg(const wchar *Arg)
{
// Process -sc before reading any file lists.
ProcessSwitch(Arg);
if (!LogName.empty())
if (*LogName!=0)
InitLogOptions(LogName,ErrlogCharset);
}
}
else
if (Command.empty())
Command=Arg; // Need for rar.ini.
if (*Command==0)
wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
}
#endif
@@ -251,10 +231,10 @@ void CommandData::ReadConfig()
Str++;
if (wcsnicomp(Str,L"switches=",9)==0)
ProcessSwitchesString(Str+9);
if (!Command.empty())
if (*Command!=0)
{
wchar Cmd[16];
wcsncpyz(Cmd,Command.c_str(),ASIZE(Cmd));
wcsncpyz(Cmd,Command,ASIZE(Cmd));
wchar C0=toupperw(Cmd[0]);
wchar C1=toupperw(Cmd[1]);
if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
@@ -274,19 +254,14 @@ void CommandData::ReadConfig()
#if !defined(SFX_MODULE)
void CommandData::ProcessSwitchesString(const std::wstring &Str)
void CommandData::ProcessSwitchesString(const wchar *Str)
{
std::wstring Par;
std::wstring::size_type Pos=0;
while (GetCmdParam(Str,Pos,Par))
wchar *Par;
while ((Str=AllocCmdParam(Str,&Par))!=NULL)
{
if (IsSwitch(Par[0]))
ProcessSwitch(&Par[1]);
else
{
mprintf(St(MSwSyntaxError),Par.c_str());
ErrHandler.Exit(RARX_USERERROR);
}
if (IsSwitch(*Par))
ProcessSwitch(Par+1);
free(Par);
}
}
#endif
@@ -296,9 +271,6 @@ void CommandData::ProcessSwitchesString(const std::wstring &Str)
void CommandData::ProcessSwitch(const wchar *Switch)
{
if (LargePageAlloc::ProcessSwitch(this,Switch))
return;
switch(toupperw(Switch[0]))
{
case '@':
@@ -337,27 +309,13 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'I':
IgnoreGeneralAttr=true;
break;
case 'M':
switch(toupperw(Switch[2]))
{
case 0:
case 'S':
ArcMetadata=ARCMETA_SAVE;
break;
case 'R':
ArcMetadata=ARCMETA_RESTORE;
break;
default:
BadSwitch(Switch);
break;
}
case 'N': // Reserved for archive name.
break;
case 'O':
AddArcOnly=true;
break;
case 'P':
// Convert slashes here than before every comparison.
SlashToNative(Switch+2,ArcPath);
wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
break;
case 'S':
SyncFiles=true;
@@ -368,14 +326,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
break;
case 'C':
if (Switch[2]!=0)
{
if (wcsicomp(Switch+1,L"FG-")==0)
ConfigDisabled=true;
else
BadSwitch(Switch);
}
else
if (Switch[2]==0)
switch(toupperw(Switch[1]))
{
case '-':
@@ -387,15 +338,10 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'L':
ConvertNames=NAMES_LOWERCASE;
break;
default:
BadSwitch(Switch);
break;
}
break;
case 'D':
if (Switch[2]!=0)
BadSwitch(Switch);
else
if (Switch[2]==0)
switch(toupperw(Switch[1]))
{
case 'S':
@@ -407,9 +353,6 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'F':
DeleteFiles=true;
break;
default:
BadSwitch(Switch);
break;
}
break;
case 'E':
@@ -430,13 +373,6 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case '3':
ExclPath=EXCL_ABSPATH;
break;
case '4':
// Convert slashes here than before every comparison.
SlashToNative(Switch+3,ExclArcPath);
break;
default:
BadSwitch(Switch);
break;
}
break;
default:
@@ -463,17 +399,13 @@ void CommandData::ProcessSwitch(const wchar *Switch)
EncryptHeaders=true;
if (Switch[2]!=0)
{
// We use this code for other archive formats too, so MAXPASSWORD
// instead of MAXPASSWORD_RAR.
if (wcslen(Switch+2)>=MAXPASSWORD)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
Password.Set(Switch+2);
cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
else
if (!Password.IsSet())
{
uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL);
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
eprintf(L"\n");
}
break;
@@ -485,7 +417,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'I':
if (wcsnicomp(Switch+1,L"LOG",3)==0)
{
LogName=Switch[4]!=0 ? Switch+4:DefLogName;
wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
break;
}
if (wcsnicomp(Switch+1,L"SND",3)==0)
@@ -503,7 +435,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
if (wcsnicomp(Switch+1,L"EML",3)==0)
{
EmailTo=Switch[4]!=0 ? Switch+4:L"@";
wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
break;
}
if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv.
@@ -610,14 +542,12 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
switch(toupperw(*(Str++)))
{
// case 'T': Type=FILTER_TEXT; break;
case 'T': Type=FILTER_PPM; break;
case 'E': Type=FILTER_E8; break;
case 'D': Type=FILTER_DELTA; break;
// case 'A': Type=FILTER_AUDIO; break;
// case 'C': Type=FILTER_RGB; break;
// case 'R': Type=FILTER_ARM; break;
case 'L': Type=FILTER_LONGRANGE; break;
case 'X': Type=FILTER_EXHAUSTIVE; break;
case 'A': Type=FILTER_AUDIO; break;
case 'C': Type=FILTER_RGB; break;
case 'R': Type=FILTER_ARM; break;
}
if (*Str=='+' || *Str=='-')
State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE;
@@ -627,74 +557,42 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
}
break;
case 'D':
{
bool SetDictLimit=toupperw(Switch[2])=='X';
uint64 Size=atoiw(Switch+(SetDictLimit ? 3 : 2));
wchar LastChar=toupperw(Switch[wcslen(Switch)-1]);
if (IsDigit(LastChar))
LastChar=SetDictLimit ? 'G':'M'; // Treat -md128 as -md128m and -mdx32 as -mdx32g.
switch(LastChar)
{
case 'K':
Size*=1024;
break;
case 'M':
Size*=1024*1024;
break;
case 'G':
Size*=1024*1024*1024;
break;
default:
BadSwitch(Switch);
}
// 2023.07.22: For 4 GB and less we also check that it is power of 2,
// so archives are compatible with RAR 5.0+.
// We allow Size>PACK_MAX_DICT here, so we can use -md[x] to unpack
// archives created by future versions with higher PACK_MAX_DICT.
uint Flags;
if ((Size=Archive::GetWinSize(Size,Flags))==0 ||
Size<=0x100000000ULL && !IsPow2(Size))
BadSwitch(Switch);
else
if (SetDictLimit)
WinSizeLimit=Size;
else
{
WinSize=Size;
}
}
break;
case 'E':
if (toupperw(Switch[2])=='S' && Switch[3]==0)
SkipEncrypted=true;
break;
case 'L':
if (toupperw(Switch[2])=='P')
{
UseLargePages=true;
if (!LargePageAlloc::IsPrivilegeAssigned() && LargePageAlloc::AssignConfirmation())
{
LargePageAlloc::AssignPrivilege();
// Quit immediately. We do not want to interrupt the current copy
// archive processing with reboot after assigning privilege.
SetupComplete=true;
}
}
break;
case 'M':
break;
case 'D':
break;
case 'S':
GetBriefMaskList(Switch[2]==0 ? DefaultStoreList:Switch+2,StoreArgs);
{
wchar StoreNames[1024];
wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames));
wchar *Names=StoreNames;
while (*Names!=0)
{
wchar *End=wcschr(Names,';');
if (End!=NULL)
*End=0;
if (*Names=='.')
Names++;
wchar Mask[NM];
if (wcspbrk(Names,L"*?.")==NULL)
swprintf(Mask,ASIZE(Mask),L"*.%ls",Names);
else
wcsncpyz(Mask,Names,ASIZE(Mask));
StoreArgs.AddString(Mask);
if (End==NULL)
break;
Names=End+1;
}
}
break;
#ifdef RAR_SMP
case 'T':
Threads=atoiw(Switch+2);
if (Threads>MaxPoolThreads || Threads<1)
BadSwitch(Switch);
else
{
}
break;
#endif
default:
@@ -740,31 +638,8 @@ void CommandData::ProcessSwitch(const wchar *Switch)
#ifdef SAVE_LINKS
case 'L':
SaveSymLinks=true;
for (uint I=2;Switch[I]!=0;I++)
switch(toupperw(Switch[I]))
{
case 'A':
AbsoluteLinks=true;
break;
case '-':
SkipSymLinks=true;
break;
default:
BadSwitch(Switch);
break;
}
break;
#endif
#ifdef PROPAGATE_MOTW
case 'M':
{
MotwAllFields=Switch[2]=='1';
const wchar *Sep=wcschr(Switch+2,'=');
if (Switch[2]=='-')
MotwList.Reset();
else
GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,MotwList);
}
if (toupperw(Switch[2])=='A')
AbsoluteLinks=true;
break;
#endif
#ifdef _WIN_ALL
@@ -773,10 +648,6 @@ void CommandData::ProcessSwitch(const wchar *Switch)
AllowIncompatNames=true;
break;
#endif
case 'P':
ExtrPath=Switch+2;
AddEndSlash(ExtrPath);
break;
case 'R':
Overwrite=OVERWRITE_AUTORENAME;
break;
@@ -796,13 +667,11 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'P':
if (Switch[1]==0)
{
uiGetPassword(UIPASSWORD_GLOBAL,L"",&Password,NULL);
uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
eprintf(L"\n");
}
else
{
if (wcslen(Switch+1)>=MAXPASSWORD)
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
Password.Set(Switch+1);
cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
@@ -869,52 +738,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
switch(toupperw(Switch[1]))
{
case 0:
case '=':
Solid|=SOLID_NORMAL;
if (Switch[1]=='=')
{
uint Par=0;
for (const wchar *S=Switch+2;*S!=0;S++)
{
if (IsDigit(*S))
Par=Par*10+*S-'0';
switch(toupperw(*S))
{
case '-':
Solid=SOLID_NONE;
break;
case 'D':
Solid|=SOLID_VOLUME_DEPENDENT;
break;
case 'E':
Solid|=SOLID_FILEEXT;
break;
case 'F':
Solid|=SOLID_COUNT;
SolidCount=Par;
break;
case 'K':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL;
break;
case 'M':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL*1024LL;
break;
case 'G':
Solid|=SOLID_BLOCK_SIZE;
SolidBlockSize=Par*1024LL*1024LL*1024LL;
break;
case 'R':
Solid=SOLID_RESET;
break;
case 'V':
Solid|=SOLID_VOLUME_INDEPENDENT;
break;
}
}
}
break;
case '-':
Solid=SOLID_NONE;
@@ -928,22 +752,13 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'D':
Solid|=SOLID_VOLUME_DEPENDENT;
break;
case 'I':
ProhibitConsoleInput();
// We do not assign the archive name automatically for -si
// if archive name is omitted and require the archive name to
// present always. Otherwise for"type arc.rar|rar x -si arc2.rar"
// if arc2.rar is a dummy archive name or file inside of arc.rar,
// which needs to be extracted.
UseStdin=Switch[2] ? Switch+2:L"stdin";
break;
case 'L':
if (IsDigit(Switch[2]))
FileSizeLess=GetModSize(Switch+2,1);
FileSizeLess=atoilw(Switch+2);
break;
case 'M':
if (IsDigit(Switch[2]))
FileSizeMore=GetModSize(Switch+2,1);
FileSizeMore=atoilw(Switch+2);
break;
case 'C':
{
@@ -1001,6 +816,12 @@ void CommandData::ProcessSwitch(const wchar *Switch)
case 'T':
switch(toupperw(Switch[1]))
{
case 'K':
ArcTime=ARCTIME_KEEP;
break;
case 'L':
ArcTime=ARCTIME_LATEST;
break;
case 'O':
SetTimeFilters(Switch+2,true,true);
break;
@@ -1052,8 +873,8 @@ void CommandData::ProcessSwitch(const wchar *Switch)
}
break;
case 'W':
TempPath=Switch+1;
AddEndSlash(TempPath);
wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
AddEndSlash(TempPath,ASIZE(TempPath));
break;
case 'Y':
AllYes=true;
@@ -1062,10 +883,10 @@ void CommandData::ProcessSwitch(const wchar *Switch)
if (Switch[1]==0)
{
// If comment file is not specified, we read data from stdin.
CommentFile=L"stdin";
wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
}
else
CommentFile=Switch+1;
wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
break;
case '?' :
OutHelp(RARX_SUCCESS);
@@ -1092,41 +913,34 @@ void CommandData::ProcessCommand()
#ifndef SFX_MODULE
const wchar *SingleCharCommands=L"FUADPXETK";
if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
// RAR -mlp command is the legitimate way to assign the required privilege.
if (Command.empty() && UseLargePages || SetupComplete)
return;
if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || ArcName.empty())
OutHelp(Command.empty() ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
size_t ExtPos=GetExtPos(ArcName);
const wchar *ArcExt=GetExt(ArcName);
#ifdef _UNIX
// If we want to update an archive without extension, in Windows we can use
// "arcname." and it will be treated as "arcname". In Unix "arcname"
// and "arcname." are two different names, so we check if "arcname" exists
// and do not append ".rar", allowing user to update such archive.
if (ExtPos==std::wstring::npos && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
ArcName+=L".rar";
if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#else
if (ExtPos==std::wstring::npos)
ArcName+=L".rar";
if (ArcExt==NULL)
wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#endif
// Treat arcname.part1 as arcname.part1.rar.
if (ExtPos!=std::wstring::npos && wcsnicomp(&ArcName[ExtPos],L".part",5)==0 &&
IsDigit(ArcName[ExtPos+5]) && !FileExist(ArcName))
if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) &&
!FileExist(ArcName))
{
std::wstring Name=ArcName+L".rar";
wchar Name[NM];
wcsncpyz(Name,ArcName,ASIZE(Name));
wcsncatz(Name,L".rar",ASIZE(Name));
if (FileExist(Name))
ArcName=Name;
wcsncpyz(ArcName,Name,ASIZE(ArcName));
}
if (wcschr(L"AFUMD",Command[0])==NULL && UseStdin.empty())
if (wcschr(L"AFUMD",*Command)==NULL)
{
if (GenerateArcName)
{
const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask;
GenerateArchiveName(ArcName,Mask,false);
GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false);
}
StringList ArcMasks;
@@ -1160,22 +974,12 @@ void CommandData::ProcessCommand()
OutHelp(RARX_USERERROR);
#endif
}
// Since messages usually include '\n' in the beginning, we also issue
// the final '\n'. It is especially important in Unix, where otherwise
// the shell can display the prompt on the same line as the last message.
// mprintf is blocked with -idq and if error messages had been displayed
// in this mode, we use eprintf to separate them from shell prompt.
// If nothing was displayed with -idq, we avoid the excessive empty line.
if (!BareOutput)
if (MsgStream==MSG_ERRONLY && IsConsoleOutputPresent())
eprintf(L"\n");
else
mprintf(L"\n");
mprintf(L"\n");
}
void CommandData::AddArcName(const std::wstring &Name)
void CommandData::AddArcName(const wchar *Name)
{
ArcNames.AddString(Name);
}
@@ -1187,15 +991,9 @@ bool CommandData::GetArcName(wchar *Name,int MaxSize)
}
bool CommandData::GetArcName(std::wstring &Name)
{
return ArcNames.GetString(Name);
}
bool CommandData::IsSwitch(int Ch)
{
#ifdef _WIN_ALL
#if defined(_WIN_ALL) || defined(_EMX)
return Ch=='-' || Ch=='/';
#else
return Ch=='-';
@@ -1221,7 +1019,7 @@ uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
case 'V':
Attr|=S_IFCHR;
break;
#elif defined(_WIN_ALL)
#elif defined(_WIN_ALL) || defined(_EMX)
case 'R':
Attr|=0x1;
break;
@@ -1245,6 +1043,21 @@ uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
#ifndef SFX_MODULE
bool CommandData::CheckWinSize()
{
// Define 0x100000000 as macro to avoid troubles with older compilers.
const uint64 MaxDictSize=INT32TO64(1,0);
// Limit the dictionary size to 4 GB.
for (uint64 I=0x10000;I<=MaxDictSize;I*=2)
if (WinSize==I)
return true;
WinSize=0x400000;
return false;
}
#endif
#ifndef SFX_MODULE
void CommandData::ReportWrongSwitches(RARFORMAT Format)
{
@@ -1274,57 +1087,3 @@ void CommandData::ReportWrongSwitches(RARFORMAT Format)
}
}
#endif
// Get size for string with optional trailing modifiers like "100m".
int64 CommandData::GetModSize(const wchar *S,uint DefMultiplier)
{
int64 Size=0,FloatingDivider=0;
for (uint I=0;S[I]!=0;I++)
if (IsDigit(S[I]))
{
Size=Size*10+S[I]-'0';
FloatingDivider*=10;
}
else
if (S[I]=='.')
FloatingDivider=1;
if (*S!=0)
{
const wchar *ModList=L"bBkKmMgGtT";
const wchar *Mod=wcschr(ModList,S[wcslen(S)-1]);
if (Mod==nullptr)
Size*=DefMultiplier;
else
for (ptrdiff_t I=2;I<=Mod-ModList;I+=2)
Size*=((Mod-ModList)&1)!=0 ? 1000:1024;
}
if (FloatingDivider!=0)
Size/=FloatingDivider;
return Size;
}
// Treat the list like rar;zip as *.rar;*.zip for -ms and similar switches.
void CommandData::GetBriefMaskList(const std::wstring &Masks,StringList &Args)
{
size_t Pos=0;
while (Pos<Masks.size())
{
if (Masks[Pos]=='.')
Pos++;
size_t EndPos=Masks.find(';',Pos);
std::wstring Mask=Masks.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos);
if (Mask.find_first_of(L"*?.")==std::wstring::npos)
Mask.insert(0,L"*.");
Args.AddString(Mask);
if (EndPos==std::wstring::npos)
break;
Pos=EndPos+1;
}
}

View File

@@ -1,14 +1,8 @@
#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"
#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx"
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
@@ -24,7 +18,6 @@ class CommandData:public RAROptions
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;
@@ -35,69 +28,43 @@ class CommandData:public RAROptions
void Init();
void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
void ParseArg(const wchar *ArgW);
void ParseArg(wchar *ArgW);
void ParseDone();
void ParseEnvVar();
void ReadConfig();
void PreprocessArg(const wchar *Arg);
void ProcessSwitchesString(const std::wstring &Str);
void ProcessSwitchesString(const wchar *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 ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
static bool CheckArgs(StringList *Args,bool Dir,const wchar *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);
bool Flags,wchar *MatchedArg,uint MatchedArgSize);
void ProcessCommand();
void AddArcName(const std::wstring &Name);
void AddArcName(const wchar *Name);
bool GetArcName(wchar *Name,int MaxSize);
bool GetArcName(std::wstring &Name);
bool CheckWinSize();
int GetRecoverySize(const wchar *Str,int DefSize);
#ifndef SFX_MODULE
void ReportWrongSwitches(RARFORMAT Format);
#endif
void GetBriefMaskList(const std::wstring &Masks,StringList &Args);
wchar Command[NM+16];
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;
wchar ArcName[NM];
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

View File

@@ -1,7 +1,7 @@
// 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)
bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
{
if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
return true;
@@ -13,21 +13,17 @@ bool CommandData::ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFul
}
bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode)
bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode)
{
std::wstring Name,FullName,CurMask;
ConvertPath(&CheckName,&Name);
wchar *Name=ConvertPath(CheckName,NULL,0);
wchar FullName[NM];
wchar CurMask[NM];
*FullName=0;
Args->Rewind();
while (Args->GetString(CurMask))
while (Args->GetString(CurMask,ASIZE(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.
wchar *LastMaskChar=PointToLastChar(CurMask);
bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
if (Dir)
{
@@ -37,33 +33,16 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN
// We process the directory and have the directory exclusion mask.
// So let's convert "mask\" to "mask" and process it normally.
CurMask.pop_back();
*LastMaskChar=0;
}
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;
// REMOVED, we want -npath\* to match empty folders too.
// If mask has wildcards in name part and does not have the trailing
// '\' character, we cannot use it for directories.
// if (IsWildcard(PointToName(CurMask)))
// continue;
}
}
else
@@ -75,7 +54,7 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN
// is excluded from further scanning.
if (DirMask)
CurMask+=L"*";
wcsncatz(CurMask,L"*",ASIZE(CurMask));
}
#ifndef SFX_MODULE
@@ -87,20 +66,19 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN
// correctly. Moreover, removing "*\" from mask would break
// the comparison, because now all names have the path.
if (FullName.empty())
ConvertNameToFull(CheckName,FullName);
if (*FullName==0)
ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
if (CmpName(CurMask,FullName,MatchMode))
return true;
}
else
#endif
{
std::wstring CurName=Name;
wchar NewName[NM+2],*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);
wchar *CmpMask=ConvertPath(CurMask,NULL,0);
if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
{
@@ -108,9 +86,10 @@ bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckN
// 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;
NewName[0]='.';
NewName[1]=CPATHDIVIDER;
wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
CurName=NewName;
}
if (CmpName(CmpMask,CurName,MatchMode))
@@ -283,8 +262,6 @@ bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
// 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)
@@ -298,10 +275,10 @@ bool CommandData::SizeCheck(int64 Size)
// 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)
bool Flags,wchar *MatchedArg,uint MatchedArgSize)
{
if (MatchedArg!=NULL)
MatchedArg->clear();
if (MatchedArg!=NULL && MatchedArgSize>0)
*MatchedArg=0;
bool Dir=FileHead.Dir;
if (ExclCheck(FileHead.FileName,Dir,false,true))
return 0;
@@ -310,28 +287,23 @@ int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchTy
return 0;
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
return 0;
if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
(!FileHead.Dir || !InclDir))
if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 ||
FileHead.Dir && !InclDir))
return 0;
if (!Dir && SizeCheck(FileHead.UnpSize))
return 0;
#endif
std::wstring ArgName;
wchar *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.
for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
if (CmpName(ArgName,FileHead.FileName,MatchType))
{
if (ExactMatch!=NULL)
*ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
if (MatchedArg!=NULL)
*MatchedArg=ArgName;
wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
return StringCount;
}
}
return 0;
}

View File

@@ -2,7 +2,7 @@ void CommandData::OutTitle()
{
if (BareOutput || DisableCopyright)
return;
#ifdef SFX_MODULE
#if defined(__GNUC__) && defined(SFX_MODULE)
mprintf(St(MCopyrightS));
#else
#ifndef SILENT
@@ -61,24 +61,26 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
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
MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
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
MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
};
bool Found=false;
for (uint J=0;J<ASIZE(Win32Only);J++)
@@ -90,18 +92,11 @@ void CommandData::OutHelp(RAR_EXIT ExitCode)
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 !defined(_WIN_ALL) && !defined(_EMX)
if (CmpMSGID(Help[I],MCHelpSwAC))
continue;
#endif

View File

@@ -1,8 +1,8 @@
inline byte RangeCoder::GetChar()
inline unsigned int RangeCoder::GetChar()
{
return UnpackRead->GetChar();
return(UnpackRead->GetChar());
}
@@ -11,8 +11,8 @@ void RangeCoder::InitDecoder(Unpack *UnpackRead)
RangeCoder::UnpackRead=UnpackRead;
low=code=0;
range=0xffffffff;
for (uint i = 0; i < 4; i++)
range=uint(-1);
for (int i=0;i < 4;i++)
code=(code << 8) | GetChar();
}

View File

@@ -11,7 +11,7 @@ class RangeCoder
inline uint GetCurrentShiftCount(uint SHIFT);
inline void Decode();
inline void PutChar(unsigned int c);
inline byte GetChar();
inline unsigned int GetChar();
uint low, code, range;
struct SUBRANGE

View File

@@ -17,16 +17,13 @@ class PackDef
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 DC = 64;
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 HUFF_TABLE_SIZE = NC + DC + RC + LDC;
static const uint BC = 20;
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
@@ -45,6 +42,10 @@ class PackDef
// Largest alphabet size among all values listed above.
static const uint LARGEST_TABLE_SIZE = 306;
enum {
CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE,
CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA
};
};
@@ -52,10 +53,7 @@ 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
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE
};
#endif

View File

@@ -3,8 +3,8 @@
static MESSAGE_TYPE MsgStream=MSG_STDOUT;
static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
static bool ProhibitInput=false;
static bool ConsoleOutputPresent=false;
const int MaxMsgSize=2*NM+2048;
static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
@@ -61,53 +61,47 @@ void SetConsoleRedirectCharset(RAR_CHARSET 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);
// This buffer is for format string only, not for entire output,
// so it can be short enough.
wchar fmtw[1024];
PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
#ifdef _WIN_ALL
safebuf wchar Msg[MaxMsgSize];
if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
{
HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
DWORD Written;
if (RedirectCharset==RCH_UNICODE)
WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL);
WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
else
{
// Avoid Unicode for redirect in Windows, it does not work with pipes.
std::string MsgA;
safebuf char MsgA[MaxMsgSize];
if (RedirectCharset==RCH_UTF8)
WideToUtf(s,MsgA);
WideToUtf(Msg,MsgA,ASIZE(MsgA));
else
WideToChar(s,MsgA);
WideToChar(Msg,MsgA,ASIZE(MsgA));
if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding.
CharToOemA(MsgA,MsgA); // 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);
WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
}
return;
}
// MSVC2008 vfwprintf writes every character to console separately
// and it is too slow. We use direct WriteConsole call instead.
vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
DWORD Written;
WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL);
WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
#else
fputws(s.c_str(),dest);
vfwprintf(dest,fmtw,arglist);
// We do not use setbuf(NULL) in Unix (see comments in InitConsole).
fflush(dest);
#endif
@@ -147,109 +141,81 @@ void eprintf(const wchar *fmt,...)
#ifndef SILENT
static void QuitIfInputProhibited()
static void GetPasswordText(wchar *Str,uint MaxLength)
{
// 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 (MaxLength==0)
return;
if (StdinRedirected)
getwstr(Str); // Read from pipe or redirected file.
getwstr(Str,MaxLength); // 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.
HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
DWORD ConInMode,ConOutMode;
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]));
GetConsoleMode(hConIn,&ConInMode);
GetConsoleMode(hConOut,&ConOutMode);
SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
Str[Read]=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.
SetConsoleMode(hConOut,ConOutMode);
#else
std::vector<char> StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters.
#ifdef __VMS
fgets(StrA.data(),StrA.size()-1,stdin);
char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters.
#if defined(_EMX) || defined (__VMS)
fgets(StrA,ASIZE(StrA)-1,stdin);
#elif defined(__sun)
strncpyz(StrA.data(),getpassphrase(""),StrA.size());
strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
#else
strncpyz(StrA.data(),getpass(""),StrA.size());
strncpyz(StrA,getpass(""),ASIZE(StrA));
#endif
CharToWide(StrA.data(),Str);
cleandata(StrA.data(),StrA.size()*sizeof(StrA[0]));
CharToWide(StrA,Str,MaxLength);
cleandata(StrA,sizeof(StrA));
#endif
}
Str[MaxLength-1]=0;
RemoveLF(Str);
}
#endif
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password)
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
{
if (!StdinRedirected)
uiAlarm(UIALARM_QUESTION);
while (true)
{
// if (!StdinRedirected)
if (!StdinRedirected)
if (Type==UIPASSWORD_GLOBAL)
eprintf(L"\n%s: ",St(MAskPsw));
else
eprintf(St(MAskPswFor),FileName.c_str());
eprintf(St(MAskPswFor),FileName);
std::wstring PlainPsw;
GetPasswordText(PlainPsw);
if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL)
wchar PlainPsw[MAXPASSWORD];
GetPasswordText(PlainPsw,ASIZE(PlainPsw));
if (*PlainPsw==0 && 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)
wchar CmpStr[MAXPASSWORD];
GetPasswordText(CmpStr,ASIZE(CmpStr));
if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
{
eprintf(St(MNotMatchPsw));
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
cleandata(PlainPsw,sizeof(PlainPsw));
cleandata(CmpStr,sizeof(CmpStr));
continue;
}
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
cleandata(CmpStr,sizeof(CmpStr));
}
Password->Set(PlainPsw.c_str());
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
Password->Set(PlainPsw);
cleandata(PlainPsw,sizeof(PlainPsw));
break;
}
return true;
@@ -258,17 +224,12 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPas
#ifndef SILENT
void getwstr(std::wstring &str)
bool getwstr(wchar *str,size_t n)
{
// 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.
*str=0;
#if defined(_WIN_ALL)
// fgetws does not work well with non-English text in Windows,
// so we do not use it.
@@ -276,16 +237,15 @@ void getwstr(std::wstring &str)
{
// 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.
Array<char> StrA(n*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);
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");
// calling Ask(), so let's better exit.
ErrHandler.Exit(RARX_USERBREAK);
}
StrA[ReadSize]=0;
@@ -294,32 +254,22 @@ void getwstr(std::wstring &str)
// 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.
CharToWide(&StrA[0],str,n);
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();
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
return false;
str[ReadSize]=0;
}
#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();
if (fgetws(str,n,stdin)==NULL)
ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
#endif
RemoveLF(str);
return true;
}
#endif
@@ -333,22 +283,22 @@ int Ask(const wchar *AskStr)
{
uiAlarm(UIALARM_QUESTION);
const uint MaxItems=10;
const int MaxItems=10;
wchar Item[MaxItems][40];
uint ItemKeyPos[MaxItems],NumItems=0;
int ItemKeyPos[MaxItems],NumItems=0;
for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_'))
for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
{
wchar *CurItem=Item[NumItems];
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
wchar *EndItem=wcschr(CurItem,'_');
if (EndItem!=nullptr)
if (EndItem!=NULL)
*EndItem=0;
uint KeyPos=0,CurKey;
int KeyPos=0,CurKey;
while ((CurKey=CurItem[KeyPos])!=0)
{
bool Found=false;
for (uint I=0;I<NumItems && !Found;I++)
for (int I=0;I<NumItems && !Found;I++)
if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
Found=true;
if (!Found && CurKey!=' ')
@@ -359,19 +309,19 @@ int Ask(const wchar *AskStr)
NumItems++;
}
for (uint I=0;I<NumItems;I++)
for (int 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++)
int KeyPos=ItemKeyPos[I];
for (int 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 Str[50];
getwstr(Str,ASIZE(Str));
wchar Ch=toupperw(Str[0]);
for (uint I=0;I<NumItems;I++)
for (int I=0;I<NumItems;I++)
if (Ch==Item[I][ItemKeyPos[I]])
return I+1;
return 0;
@@ -379,11 +329,11 @@ int Ask(const wchar *AskStr)
#endif
static bool IsCommentUnsafe(const std::wstring &Data)
static bool IsCommentUnsafe(const wchar *Data,size_t Size)
{
for (size_t I=0;I<Data.size();I++)
for (size_t I=0;I<Size;I++)
if (Data[I]==27 && Data[I+1]=='[')
for (size_t J=I+2;J<Data.size();J++)
for (size_t J=I+2;J<Size;J++)
{
// Return true for <ESC>[{key};"{string}"p used to redefine
// a keyboard key on some terminals.
@@ -396,21 +346,18 @@ static bool IsCommentUnsafe(const std::wstring &Data)
}
void OutComment(const std::wstring &Comment)
void OutComment(const wchar *Comment,size_t Size)
{
if (IsCommentUnsafe(Comment))
if (IsCommentUnsafe(Comment,Size))
return;
const size_t MaxOutSize=0x400;
for (size_t I=0;I<Comment.size();I+=MaxOutSize)
for (size_t I=0;I<Size;I+=MaxOutSize)
{
size_t CopySize=Min(MaxOutSize,Comment.size()-I);
mprintf(L"%s",Comment.substr(I,CopySize).c_str());
wchar Msg[MaxOutSize+1];
size_t CopySize=Min(MaxOutSize,Size-I);
wcsncpy(Msg,Comment+I,CopySize);
Msg[CopySize]=0;
mprintf(L"%s",Msg);
}
mprintf(L"\n");
}
bool IsConsoleOutputPresent()
{
return ConsoleOutputPresent;
}

View File

@@ -4,12 +4,10 @@
void InitConsole();
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream);
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset);
void ProhibitConsoleInput();
void OutComment(const std::wstring &Comment);
bool IsConsoleOutputPresent();
void OutComment(const wchar *Comment,size_t Size);
#ifndef SILENT
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password);
bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
#endif
#ifdef SILENT
@@ -17,13 +15,13 @@ bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPas
inline void eprintf(const wchar *fmt,...) {}
inline void Alarm() {}
inline int Ask(const wchar *AskStr) {return 0;}
inline void getwstr(std::wstring &str) {}
inline bool getwstr(wchar *str,size_t n) {return false;}
#else
void mprintf(const wchar *fmt,...);
void eprintf(const wchar *fmt,...);
void Alarm();
int Ask(const wchar *AskStr);
void getwstr(std::wstring &str);
bool getwstr(wchar *str,size_t n);
#endif
#endif

View File

@@ -14,17 +14,7 @@
#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
static uint crc_tables[8][256]; // Tables for Slicing-by-8.
// Build the classic CRC32 lookup table.
@@ -40,19 +30,6 @@ void InitCRC32(uint *CRCTab)
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
}
@@ -60,17 +37,15 @@ 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++)
for (uint J=1;J<8;J++)
{
C=crc_tables[0][(byte)C]^(C>>8);
crc_tables[J][I]=C;
}
}
#endif
}
@@ -80,68 +55,28 @@ 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++)
// Align Data to 8 for better performance.
for (;Size>0 && ((size_t)Data & 7);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)
for (;Size>=8;Size-=8,Data+=8)
{
#ifdef BIG_ENDIAN
StartCRC ^= RawGet4(Data);
uint D1 = RawGet4(Data+4);
uint D2 = RawGet4(Data+8);
uint D3 = RawGet4(Data+12);
StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
#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);
uint NextData = *(uint32 *) (Data+4);
#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)];
StartCRC = crc_tables[7][(byte) StartCRC ] ^
crc_tables[6][(byte)(StartCRC >> 8) ] ^
crc_tables[5][(byte)(StartCRC >> 16)] ^
crc_tables[4][(byte)(StartCRC >> 24)] ^
crc_tables[3][(byte) NextData ] ^
crc_tables[2][(byte)(NextData >> 8) ] ^
crc_tables[1][(byte)(NextData >> 16)] ^
crc_tables[0][(byte)(NextData >> 24)];
}
#endif
for (;Size>0;Size--,Data++) // Process left data.
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
@@ -165,116 +100,3 @@ ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
#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

View File

@@ -11,12 +11,21 @@
CryptData::CryptData()
{
Method=CRYPT_NONE;
memset(KDF3Cache,0,sizeof(KDF3Cache));
memset(KDF5Cache,0,sizeof(KDF5Cache));
KDF3CachePos=0;
KDF5CachePos=0;
memset(CRCTab,0,sizeof(CRCTab));
}
CryptData::~CryptData()
{
cleandata(KDF3Cache,sizeof(KDF3Cache));
cleandata(KDF5Cache,sizeof(KDF5Cache));
}
void CryptData::DecryptBlock(byte *Buf,size_t Size)
@@ -47,28 +56,16 @@ 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())
if (!Password->IsSet() || Method==CRYPT_NONE)
return false;
CryptData::Method=Method;
wchar PwdW[MAXPASSWORD];
Password->Get(PwdW,ASIZE(PwdW));
// Display this warning only when encrypting. Users complained that
// it is distracting when decrypting. It still can be useful when encrypting,
// so users do not waste time to excessively long passwords.
if (Encrypt && 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
@@ -86,12 +83,12 @@ bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
SetKey30(Encrypt,Password,PwdW,Salt);
break;
case CRYPT_RAR50:
Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
break;
}
cleandata(PwdA,sizeof(PwdA));
cleandata(PwdW,sizeof(PwdW));
return Success;
return true;
}
@@ -120,7 +117,7 @@ void GetRnd(byte *RndBuf,size_t BufSize)
HCRYPTPROV hProvider = 0;
if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) != FALSE;
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
CryptReleaseContext(hProvider, 0);
}
#elif defined(_UNIX)

View File

@@ -3,8 +3,7 @@
enum CRYPT_METHOD {
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50,
CRYPT_UNKNOWN
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
};
#define SIZE_SALT50 16
@@ -31,18 +30,6 @@ class CryptData
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
@@ -52,17 +39,6 @@ class CryptData
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));
}
};
@@ -80,7 +56,7 @@ class CryptData
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);
void 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;
@@ -101,63 +77,17 @@ class CryptData
ushort Key15[4];
public:
CryptData();
~CryptData();
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
const byte *Salt,const byte *InitV,uint Lg2Cnt,
byte *HashKey,byte *PswCheck);
void SetAV15Encryption();
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,

View File

@@ -1,3 +1,5 @@
extern uint CRCTab[256];
void CryptData::SetKey13(const char *Password)
{
Key13[0]=Key13[1]=Key13[2]=0;
@@ -23,11 +25,22 @@ void CryptData::SetKey15(const char *Password)
{
byte P=Password[I];
Key15[2]^=P^CRCTab[P];
Key15[3]+=ushort(P+(CRCTab[P]>>16));
Key15[3]+=P+(CRCTab[P]>>16);
}
}
void CryptData::SetAV15Encryption()
{
InitCRC32(CRCTab);
Method=CRYPT_RAR15;
Key15[0]=0x4765;
Key15[1]=0x9021;
Key15[2]=0x7382;
Key15[3]=0x5215;
}
void CryptData::SetCmt13Encryption()
{
Method=CRYPT_RAR13;
@@ -55,7 +68,7 @@ void CryptData::Crypt15(byte *Data,size_t Count)
{
Key15[0]+=0x1234;
Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
Key15[2]-=ushort(CRCTab[(Key15[0] & 0x1fe)>>1]>>16);
Key15[2]-=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);

View File

@@ -18,9 +18,8 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co
if (!Cached)
{
byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
size_t PswLength=wcslen(PwdW);
size_t RawLength=2*PswLength;
WideToRaw(PwdW,PswLength,RawPsw,RawLength);
WideToRaw(PwdW,RawPsw,ASIZE(RawPsw));
size_t RawLength=2*wcslen(PwdW);
if (Salt!=NULL)
{
memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);

View File

@@ -21,7 +21,7 @@ static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
sha256_context ICtx;
if (ICtxOpt!=NULL && *SetIOpt)
ICtx=*ICtxOpt; // Use already calculated the first block context.
ICtx=*ICtxOpt; // Use already calculated first block context.
else
{
// This calculation is the same for all iterations with same password.
@@ -90,10 +90,10 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
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.
SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
SaltData[SaltLength + 1] = 0;
SaltData[SaltLength + 2] = 0;
SaltData[SaltLength + 3] = 1;
// First iteration: HMAC of password, salt and block index (1).
byte U1[SHA256_DIGEST_SIZE];
@@ -128,19 +128,19 @@ void pbkdf2(const byte *Pwd, size_t PwdLength,
}
bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
void 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;
return;
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 &&
if (Item->Lg2Count==Lg2Cnt && Item->Pwd==*Password &&
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
{
memcpy(Key,Item->Key,sizeof(Key));
@@ -186,7 +186,6 @@ bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
rin.Init(Encrypt, Key, 256, InitV);
cleandata(Key,sizeof(Key));
return true;
}
@@ -201,7 +200,6 @@ void ConvertHashToMAC(HashValue *Value,byte *Key)
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)
{

View File

@@ -16,7 +16,8 @@ struct DataSet
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
{
RAROpenArchiveDataEx rx{};
RAROpenArchiveDataEx rx;
memset(&rx,0,sizeof(rx));
rx.ArcName=r->ArcName;
rx.OpenMode=r->OpenMode;
rx.CmtBuf=r->CmtBuf;
@@ -31,7 +32,7 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
DataSet *Data=nullptr;
DataSet *Data=NULL;
try
{
ErrHandler.Clean();
@@ -43,21 +44,22 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
Data->Cmd.FileArgs.AddString(L"*");
Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
std::string AnsiArcName;
if (r->ArcName!=nullptr)
char AnsiArcName[NM];
*AnsiArcName=0;
if (r->ArcName!=NULL)
{
AnsiArcName=r->ArcName;
strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
#ifdef _WIN_ALL
if (!AreFileApisANSI())
OemToExt(r->ArcName,AnsiArcName);
{
OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
AnsiArcName[ASIZE(AnsiArcName)-1]=0;
}
#endif
}
std::wstring ArcName;
if (r->ArcNameW!=nullptr && *r->ArcNameW!=0)
ArcName=r->ArcNameW;
else
CharToWide(AnsiArcName,ArcName);
wchar ArcName[NM];
GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
Data->Cmd.AddArcName(ArcName);
Data->Cmd.Overwrite=OVERWRITE_ALL;
@@ -73,7 +75,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
r->OpenResult=ERAR_EOPEN;
delete Data;
return nullptr;
return NULL;
}
if (!Data->Arc.IsArchive(true))
{
@@ -88,7 +90,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
r->OpenResult=ERAR_BAD_ARCHIVE;
}
delete Data;
return nullptr;
return NULL;
}
r->Flags=0;
@@ -111,47 +113,35 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
if (Data->Arc.FirstVolume)
r->Flags|=ROADF_FIRSTVOLUME;
std::wstring CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW))
Array<wchar> CmtDataW;
if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
{
if (r->CmtBufW!=nullptr)
if (r->CmtBufW!=NULL)
{
// CmtDataW.push_back(0);
size_t Size=wcslen(CmtDataW.data())+1;
CmtDataW.Push(0);
size_t Size=wcslen(&CmtDataW[0])+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));
memcpy(r->CmtBufW,&CmtDataW[0],(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;
Array<char> CmtData(CmtDataW.Size()*4+1);
memset(&CmtData[0],0,CmtData.Size());
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
size_t Size=strlen(&CmtData[0])+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;
}
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
memcpy(r->CmtBuf,&CmtData[0],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;
}
@@ -193,7 +183,8 @@ int PASCAL RARCloseArchive(HANDLE hArcData)
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
{
struct RARHeaderDataEx X{};
struct RARHeaderDataEx X;
memset(&X,0,sizeof(X));
int Code=RARReadHeaderEx(hArcData,&X);
@@ -250,18 +241,14 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
else
return Code;
}
wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW));
wcsncpy(D->ArcNameW,Data->Arc.FileName,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));
wcsncpy(D->FileNameW,hd->FileName,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)
@@ -323,7 +310,7 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
// 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);
wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize);
D->DirTarget=hd->DirTarget;
/* added by me */
@@ -333,16 +320,7 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
if (Data->Cmd.DllError!=0)
return Data->Cmd.DllError;
// Non-fatal errors like RARX_CRC (bad header checksum) or RARX_WARNING
// can be set during a successful header read and should not be reported
// as failures here; callers use BrokenHeader flag or the listing result to
// detect them. Only propagate errors severe enough to warrant stopping.
RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
if (ErrCode==RARX_SUCCESS || ErrCode==RARX_WARNING || ErrCode==RARX_CRC)
return ERAR_SUCCESS;
return RarErrorToDll(ErrCode);
return ERAR_SUCCESS;
}
@@ -387,43 +365,45 @@ int PASCAL ProcessFile(HANDLE hArcData, int Operation, char *DestPath,
{
Data->Cmd.DllOpMode=Operation;
Data->Cmd.ExtrPath.clear();
Data->Cmd.DllDestName.clear();
*Data->Cmd.ExtrPath=0;
*Data->Cmd.DllDestName=0;
if (DestPath!=NULL)
{
std::string ExtrPathA=DestPath;
char ExtrPathA[NM];
strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2);
#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);
OemToCharA(ExtrPathA,ExtrPathA);
#endif
CharToWide(ExtrPathA,Data->Cmd.ExtrPath);
AddEndSlash(Data->Cmd.ExtrPath);
CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
}
if (DestName!=NULL)
{
std::string DestNameA=DestName;
char DestNameA[NM];
strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2);
#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);
OemToCharA(DestNameA,DestNameA);
#endif
CharToWide(DestNameA,Data->Cmd.DllDestName);
CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
}
if (DestPathW!=NULL)
{
Data->Cmd.ExtrPath=DestPathW;
AddEndSlash(Data->Cmd.ExtrPath);
wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
}
if (DestNameW!=NULL)
Data->Cmd.DllDestName=DestNameW;
wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
Data->Cmd.Command=Operation==RAR_EXTRACT ? L"X":L"T";
wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command));
Data->Cmd.Test=Operation!=RAR_EXTRACT;
if (Operation == RAR_EXTRACT_CHUNK)
{
@@ -457,10 +437,6 @@ int PASCAL ProcessFile(HANDLE hArcData, int Operation, char *DestPath,
}
// 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
@@ -490,17 +466,7 @@ int PASCAL ProcessFile(HANDLE hArcData, int Operation, char *DestPath,
{
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
if (Data->Cmd.DllError!=0)
return Data->Cmd.DllError;
// Non-fatal errors like RARX_CRC (bad header checksum) or RARX_WARNING
// can be set during a successful header read and should not cause skip/list
// operations to fail. Only propagate errors severe enough to warrant stopping.
{
RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
if (ErrCode==RARX_SUCCESS || ErrCode==RARX_WARNING || ErrCode==RARX_CRC)
return ERAR_SUCCESS;
return RarErrorToDll(ErrCode);
}
return Data->Cmd.DllError;
}
@@ -568,7 +534,7 @@ void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
#ifndef RAR_NOCRYPT
DataSet *Data=(DataSet *)hArcData;
wchar PasswordW[MAXPASSWORD];
CharToWide(Password,PasswordW,ASIZE(PasswordW));
GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
Data->Cmd.Password.Set(PasswordW);
cleandata(PasswordW,sizeof(PasswordW));
#endif
@@ -602,8 +568,6 @@ static int RarErrorToDll(RAR_EXIT ErrCode)
return ERAR_BAD_PASSWORD;
case RARX_SUCCESS:
return ERAR_SUCCESS; // 0.
case RARX_BADARC:
return ERAR_BAD_ARCHIVE;
default:
return ERAR_UNKNOWN;
}

View File

@@ -19,7 +19,6 @@
#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
@@ -33,7 +32,7 @@
#define RAR_VOL_ASK 0
#define RAR_VOL_NOTIFY 1
#define RAR_DLL_VERSION 9
#define RAR_DLL_VERSION 8
#define RAR_DLL_EXT_VERSION 1 //added by me
#define RAR_HASH_NONE 0
@@ -131,13 +130,9 @@ struct RARHeaderDataEx
unsigned int CtimeHigh;
unsigned int AtimeLow;
unsigned int AtimeHigh;
wchar_t *ArcNameEx;
unsigned int ArcNameExSize;
wchar_t *FileNameEx;
unsigned int FileNameExSize;
/* removed by me: we don't need to retain binary compatibility in case new
* fields are added, so we avoid wasting space here */
/* unsigned int Reserved[982]; */
/* unsigned int Reserved[988]; */
/* added by me */
size_t WinSize; /* window size */
};
@@ -183,14 +178,13 @@ struct RAROpenArchiveDataEx
LPARAM UserData;
unsigned int OpFlags;
wchar_t *CmtBufW;
wchar_t *MarkOfTheWeb;
/* removed by me: we don't need to retain binary compatibility */
/* unsigned int Reserved[23]; */
/* removed by me */
/* unsigned int Reserved[25]; */
};
enum UNRARCALLBACK_MESSAGES {
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
UCM_NEEDPASSWORDW,UCM_LARGEDICT
UCM_NEEDPASSWORDW
};
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);

View File

@@ -2,8 +2,8 @@
#include <commctrl.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 7, 20, 100, 1861
PRODUCTVERSION 7, 20, 100, 1861
FILEVERSION 6, 0, 2, 3610
PRODUCTVERSION 6, 0, 2, 3610
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
@@ -14,9 +14,9 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
VALUE "FileVersion", "7.20.0\0"
VALUE "ProductVersion", "7.20.0\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2026\0"
VALUE "FileVersion", "6.0.2\0"
VALUE "ProductVersion", "6.0.2\0"
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
}

View File

@@ -11,16 +11,17 @@ EncodeFileName::EncodeFileName()
void EncodeFileName::Decode(const char *Name,size_t NameSize,
const byte *EncName,size_t EncSize,
std::wstring &NameW)
void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,
wchar *NameW,size_t MaxDecSize)
{
size_t EncPos=0,DecPos=0;
byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
while (EncPos<EncSize)
while (EncPos<EncSize && DecPos<MaxDecSize)
{
if (FlagBits==0)
{
if (EncPos>=EncSize)
break;
Flags=EncName[EncPos++];
FlagBits=8;
}
@@ -29,20 +30,16 @@ void EncodeFileName::Decode(const char *Name,size_t NameSize,
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;
@@ -56,22 +53,17 @@ void EncodeFileName::Decode(const char *Name,size_t NameSize,
if (EncPos>=EncSize)
break;
byte Correction=EncName[EncPos++];
for (Length=(Length&0x7f)+2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
}
}
else
for (Length+=2;Length>0 && DecPos<NameSize;Length--,DecPos++)
{
NameW.resize(DecPos+1);
for (Length+=2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++)
NameW[DecPos]=Name[DecPos];
}
}
break;
}
Flags<<=2;
FlagBits-=2;
}
NameW[DecPos<MaxDecSize ? DecPos:MaxDecSize-1]=0;
}

View File

@@ -4,16 +4,17 @@
class EncodeFileName
{
private:
void AddFlags(byte Value,std::vector<byte> &EncName);
void AddFlags(int Value);
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);
size_t Encode(char *Name,wchar *NameW,byte *EncName);
void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize);
};
#endif

View File

@@ -26,7 +26,7 @@ void ErrorHandler::MemoryError()
}
void ErrorHandler::OpenError(const std::wstring &FileName)
void ErrorHandler::OpenError(const wchar *FileName)
{
#ifndef SILENT
OpenErrorMsg(FileName);
@@ -35,7 +35,7 @@ void ErrorHandler::OpenError(const std::wstring &FileName)
}
void ErrorHandler::CloseError(const std::wstring &FileName)
void ErrorHandler::CloseError(const wchar *FileName)
{
if (!UserBreak)
{
@@ -51,7 +51,7 @@ void ErrorHandler::CloseError(const std::wstring &FileName)
}
void ErrorHandler::ReadError(const std::wstring &FileName)
void ErrorHandler::ReadError(const wchar *FileName)
{
#ifndef SILENT
ReadErrorMsg(FileName);
@@ -62,13 +62,13 @@ void ErrorHandler::ReadError(const std::wstring &FileName)
}
void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit)
void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit)
{
SetErrorCode(RARX_READ);
#if !defined(SILENT) && !defined(SFX_MODULE)
if (!Silent)
{
uiMsg(UIERROR_FILEREAD,L"",FileName);
uiMsg(UIERROR_FILEREAD,UINULL,FileName);
SysErrMsg();
if (ReadErrIgnoreAll)
Ignore=true;
@@ -88,7 +88,7 @@ void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool
}
void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName)
{
#ifndef SILENT
WriteErrorMsg(ArcName,FileName);
@@ -100,7 +100,7 @@ void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &Fi
#ifdef _WIN_ALL
void ErrorHandler::WriteErrorFAT(const std::wstring &FileName)
void ErrorHandler::WriteErrorFAT(const wchar *FileName)
{
SysErrMsg();
uiMsg(UIERROR_NTFSREQUIRED,FileName);
@@ -111,7 +111,7 @@ void ErrorHandler::WriteErrorFAT(const std::wstring &FileName)
#endif
bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull)
bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull)
{
#ifndef SILENT
if (!Silent)
@@ -129,7 +129,7 @@ bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull)
}
void ErrorHandler::SeekError(const std::wstring &FileName)
void ErrorHandler::SeekError(const wchar *FileName)
{
if (!UserBreak)
{
@@ -144,16 +144,13 @@ void ErrorHandler::SeekError(const std::wstring &FileName)
void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
{
#ifndef RARDLL
va_list arglist;
va_start(arglist,fmt);
std::wstring Msg=vwstrprintf(fmt,arglist);
wchar Msg[1024];
vswprintf(Msg,ASIZE(Msg),fmt,arglist);
uiMsg(UIERROR_GENERALERRMSG,Msg);
SysErrMsg();
va_end(arglist);
#endif
}
@@ -164,31 +161,28 @@ void ErrorHandler::MemoryErrorMsg()
}
void ErrorHandler::OpenErrorMsg(const std::wstring &FileName)
void ErrorHandler::OpenErrorMsg(const wchar *FileName)
{
OpenErrorMsg(L"",FileName);
OpenErrorMsg(NULL,FileName);
}
void ErrorHandler::OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName)
{
Wait(); // Keep GUI responsive if many files cannot be opened when archiving.
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)
void ErrorHandler::CreateErrorMsg(const wchar *FileName)
{
CreateErrorMsg(L"",FileName);
CreateErrorMsg(NULL,FileName);
}
void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName)
{
uiMsg(UIERROR_FILECREATE,ArcName,FileName);
SysErrMsg();
@@ -196,13 +190,13 @@ void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring
}
void ErrorHandler::ReadErrorMsg(const std::wstring &FileName)
void ErrorHandler::ReadErrorMsg(const wchar *FileName)
{
ReadErrorMsg(L"",FileName);
ReadErrorMsg(NULL,FileName);
}
void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName)
{
uiMsg(UIERROR_FILEREAD,ArcName,FileName);
SysErrMsg();
@@ -210,7 +204,7 @@ void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &
}
void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
{
uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
SysErrMsg();
@@ -218,21 +212,21 @@ void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring
}
void ErrorHandler::ArcBrokenMsg(const std::wstring &ArcName)
void ErrorHandler::ArcBrokenMsg(const wchar *ArcName)
{
uiMsg(UIERROR_ARCBROKEN,ArcName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName)
{
uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
SetErrorCode(RARX_CRC);
}
void ErrorHandler::UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName)
void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName)
{
uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
ErrHandler.SetErrorCode(RARX_FATAL);
@@ -256,8 +250,7 @@ void ErrorHandler::SetErrorCode(RAR_EXIT Code)
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)
if (ExitCode!=RARX_BADPWD)
ExitCode=Code;
break;
case RARX_FATAL:
@@ -336,44 +329,33 @@ 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));
// Do not write "aborted" when just displaying online help.
if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
mprintf(L"\n%s\n",St(MProgAborted));
#endif
SetErrorCode(Code);
throw Code;
}
bool ErrorHandler::GetSysErrMsg(std::wstring &Msg)
bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size)
{
#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;
}
}
return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
Msg,(DWORD)Size,NULL)!=0;
#endif
#ifdef _UNIX
#if defined(_UNIX) || defined(_EMX)
if (errno!=0)
{
char *err=strerror(errno);
if (err!=NULL)
{
CharToWide(err,Msg);
CharToWide(err,Msg,Size);
return true;
}
}
@@ -385,27 +367,32 @@ bool ErrorHandler::GetSysErrMsg(std::wstring &Msg)
void ErrorHandler::SysErrMsg()
{
#ifndef SILENT
std::wstring Msg;
if (!GetSysErrMsg(Msg))
#if !defined(SFX_MODULE) && !defined(SILENT)
wchar Msg[1024];
if (!GetSysErrMsg(Msg,ASIZE(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)
wchar *CurMsg=Msg;
while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines.
{
while (Msg[Pos]=='\r' || Msg[Pos]=='\n')
Pos++;
if (Pos==Msg.size())
while (*CurMsg=='\r' || *CurMsg=='\n')
CurMsg++;
if (*CurMsg==0)
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);
wchar *EndMsg=wcschr(CurMsg,'\r');
if (EndMsg==NULL)
EndMsg=wcschr(CurMsg,'\n');
if (EndMsg!=NULL)
{
*EndMsg=0;
EndMsg++;
}
uiMsg(UIERROR_SYSERRMSG,CurMsg);
Pos=EndPos;
CurMsg=EndMsg;
}
#endif
#ifdef _UNIX
#if defined(_UNIX) || defined(_EMX)
uiMsg(UIERROR_SYSERRMSG,Msg);
#endif

View File

@@ -16,7 +16,6 @@ enum RAR_EXIT // RAR exit code.
RARX_NOFILES = 10,
RARX_BADPWD = 11,
RARX_READ = 12,
RARX_BADARC = 13,
RARX_USERBREAK = 255
};
@@ -34,26 +33,26 @@ class ErrorHandler
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 OpenError(const wchar *FileName);
void CloseError(const wchar *FileName);
void ReadError(const wchar *FileName);
void AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit);
void WriteError(const wchar *ArcName,const wchar *FileName);
void WriteErrorFAT(const wchar *FileName);
bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
void SeekError(const wchar *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 OpenErrorMsg(const wchar *FileName);
void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
void CreateErrorMsg(const wchar *FileName);
void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
void ReadErrorMsg(const wchar *FileName);
void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
void ArcBrokenMsg(const wchar *ArcName);
void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
void Exit(RAR_EXIT ExitCode);
void SetErrorCode(RAR_EXIT Code);
RAR_EXIT GetErrorCode() {return ExitCode;}
@@ -61,7 +60,7 @@ class ErrorHandler
void SetSignalHandlers(bool Enable);
void Throw(RAR_EXIT Code);
void SetSilent(bool Mode) {Silent=Mode;}
bool GetSysErrMsg(std::wstring &Msg);
bool GetSysErrMsg(wchar *Msg,size_t Size);
void SysErrMsg();
int GetSystemErrorCode();
void SetSystemErrorCode(int Code);

View File

@@ -19,13 +19,19 @@
// RAR2 service header extra records.
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{
#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
#ifdef _UNIX
case UO_HEAD:
if (Cmd->ProcessOwners)
ExtractUnixOwner20(Arc,Name);
break;
#endif
#ifdef _WIN_ALL
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
@@ -33,19 +39,19 @@ void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
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)
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
{
#ifdef _UNIX
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
ExtractUnixOwner30(Arc,Name.c_str());
ExtractUnixOwner30(Arc,Name);
#endif
#ifdef _WIN_ALL
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
@@ -57,7 +63,7 @@ void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
// Extra data stored directly in file header.
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
{
#ifdef _UNIX
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
@@ -68,34 +74,36 @@ void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
// Calculate the number of path components except \. and \..
static int CalcAllowedDepth(const std::wstring &Name)
// Calculate a number of path components except \. and \..
static int CalcAllowedDepth(const wchar *Name)
{
int AllowedDepth=0;
for (size_t I=0;I<Name.size();I++)
if (IsPathDiv(Name[I]))
while (*Name!=0)
{
if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
{
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);
bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
if (!Dot && !Dot2)
AllowedDepth++;
else
if (Dot2)
AllowedDepth--;
}
return AllowedDepth < 0 ? 0 : AllowedDepth;
Name++;
}
return AllowedDepth;
}
// Check if all existing path components are directories and not links.
static bool LinkInPath(std::wstring Path)
static bool LinkInPath(const wchar *Name)
{
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]))
wchar Path[NM];
if (wcslen(Name)>=ASIZE(Path))
return true; // It should not be that long, skip.
wcsncpyz(Path,Name,ASIZE(Path));
for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
if (IsPathDiv(*s))
{
Path.erase(I);
*s=0;
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
return true;
@@ -104,7 +112,7 @@ static bool LinkInPath(std::wstring Path)
}
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName)
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *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
@@ -114,22 +122,19 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wst
// Number of ".." in link target.
int UpLevels=0;
for (uint Pos=0;Pos<TargetName.size();Pos++)
for (int Pos=0;*TargetName!=0;Pos++)
{
bool Dot2=TargetName[Pos]=='.' && TargetName[Pos+1]=='.' &&
(IsPathDiv(TargetName[Pos+2]) || TargetName[Pos+2]==0) &&
(Pos==0 || IsPathDiv(TargetName[Pos-1]));
bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
(IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
(Pos==0 || IsPathDiv(*(TargetName-1)));
if (Dot2)
UpLevels++;
TargetName++;
}
// If link target includes "..", it must not have another links in its
// source path, because they can bypass our safety check. For example,
// If link target includes "..", it must not have another links
// in the 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.
// or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;
@@ -142,12 +147,12 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wst
// 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)
size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
{
while (IsPathDiv(PrepSrcName[ExtrPathLength]))
ExtrPathLength++;
PrepSrcName.erase(0,ExtrPathLength);
PrepSrcName+=ExtrPathLength;
while (IsPathDiv(*PrepSrcName))
PrepSrcName++;
}
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
@@ -155,30 +160,19 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wst
}
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink)
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
{
// 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);
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead);
#elif defined(_WIN_ALL)
return ExtractUnixLink50(Cmd,LinkName,&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);
return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
#endif
return false;
}

View File

@@ -1,19 +1,23 @@
#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);
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const std::wstring &FileName);
void SetUnixOwner(Archive &Arc,const wchar *FileName);
#endif
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting);
bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
std::wstring GetStreamNameNTFS(Archive &Arc);
void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
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);
#ifdef _WIN_ALL
bool SetPrivilege(LPCTSTR PrivName);
#endif
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -6,44 +6,24 @@ 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);
bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
#ifdef RARDLL
bool ExtrDllGetPassword();
#else
bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd);
bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
#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);
void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
bool ExtrCreateFile(Archive &Arc,File &CurFile);
bool CheckUnpVer(Archive &Arc,const wchar *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);
bool DetectStartVolume(const wchar *VolName,bool NewNumbering);
void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize);
#endif
bool CheckWinLimit(Archive &Arc,std::wstring &ArcFileName);
RarTime StartTime; // Time when extraction started.
@@ -66,21 +46,12 @@ class CmdExtract
// any wrong password hints.
bool AnySolidDataUnpackedWell;
std::wstring ArcName;
wchar ArcName[NM];
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;
wchar DestFileName[NM];
bool PasswordCancelled;
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif

View File

@@ -40,21 +40,22 @@ bool CmdExtract::ExtractCurrentFileChunkInit(Archive &Arc,
if (Arc.FileHead.SplitBefore && FirstFile)
{
std::wstring CurVolName=ArcName;
VolNameToFirstName(ArcName,ArcName,Arc.NewNumbering);
wchar CurVolName[NM];
wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering);
if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
{
// If first volume name does not match the current name and if such
// volume name really exists, let's unpack from this first volume.
ArcName.clear();
*ArcName=0;
Repeat=true;
ErrHandler.SetErrorCode(RARX_WARNING);
/* Actually known. The problem is that the file doesn't start on this volume. */
Cmd->DllError = ERAR_UNKNOWN;
return false;
}
ArcName=CurVolName;
wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
}
DataIO.UnpVolume=Arc.FileHead.SplitAfter;
@@ -72,16 +73,16 @@ bool CmdExtract::ExtractCurrentFileChunkInit(Archive &Arc,
}
}
if (!Cmd->DllDestName.empty())
if (*Cmd->DllDestName!=0)
{
DestFileName=Cmd->DllDestName;
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
// Do we need this code?
// if (Cmd->DllOpMode!=RAR_EXTRACT)
// ExtrFile=false;
}
std::wstring ArcFileName;
ConvertPath(&Arc.FileHead.FileName,&ArcFileName);
wchar ArcFileName[NM];
ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
if (!CheckUnpVer(Arc,ArcFileName))
{
ErrHandler.SetErrorCode(RARX_FATAL);

View File

@@ -1,9 +1,9 @@
#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,
// It is useful we 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 FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
@@ -29,7 +29,7 @@ bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
// 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));
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
if (Choice==UIASKREP_R_REPLACE)
break;
@@ -44,82 +44,95 @@ bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
}
// 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.
// so we preserve existing file permissions such as NTFS permissions.
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);
CreatePath(Name,true);
return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
}
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
{
wchar NewName[NM];
size_t NameLength=wcslen(Name);
wchar *Ext=GetExt(Name);
if (Ext==NULL)
Ext=Name+NameLength;
for (uint FileVer=1;;FileVer++)
{
swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
if (!FileExist(NewName))
{
wcsncpyz(Name,NewName,MaxNameSize);
break;
}
if (FileVer>=1000000)
return false;
}
return true;
}
#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)
bool UpdateExistingShortName(const wchar *Name)
{
DWORD Res=GetLongPathName(Name.c_str(),NULL,0);
if (Res==0)
wchar LongPathName[NM];
DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
if (Res==0 || Res>=ASIZE(LongPathName))
return false;
std::vector<wchar> LongPathBuf(Res);
Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size());
if (Res==0 || Res>=LongPathBuf.size())
wchar ShortPathName[NM];
Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
if (Res==0 || Res>=ASIZE(ShortPathName))
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);
wchar *LongName=PointToName(LongPathName);
wchar *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 ||
if (*ShortName==0 || 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)
wchar NewName[NM];
*NewName=0;
for (int I=0;I<10000 && *NewName==0;I+=123)
{
// Here we copy the path part of file to create. We'll make the temporary
// file in the same folder.
NewName=Name;
wcsncpyz(NewName,Name,ASIZE(NewName));
// Here we set the random name part.
SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I));
swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
// If such file is already exist, try next random name.
if (FileExist(NewName))
NewName.clear();
*NewName=0;
}
// If we could not generate the name not used by any other file, we return.
if (NewName.empty())
if (*NewName==0)
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);
wchar FullName[NM];
wcsncpyz(FullName,Name,ASIZE(FullName));
SetName(FullName,LongName,ASIZE(FullName));
// Rename the existing file to randomly generated name. Normally it changes
// the short name too.
if (!MoveFile(FullName.c_str(),NewName.c_str()))
if (!MoveFile(FullName,NewName))
return false;
// Now we need to create the temporary empty file with same name as
@@ -134,7 +147,7 @@ bool UpdateExistingShortName(const std::wstring &Name)
// 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());
MoveFile(NewName,FullName);
if (Created)
{
@@ -142,9 +155,9 @@ bool UpdateExistingShortName(const std::wstring &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.
// We successfully changed the short name. Maybe sometimes we'll simplify
// this function by use of SetFileShortName Windows API call.
// But SetFileShortName is not available in older Windows.
return true;
}
#endif

View File

@@ -1,12 +1,14 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
bool *UserReject,int64 FileSize=INT64NDF,
RarTime *FileTime=NULL,bool WriteOnly=false);
bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
#if defined(_WIN_ALL)
bool UpdateExistingShortName(const std::wstring &Name);
bool UpdateExistingShortName(const wchar *Name);
#endif
#endif

View File

@@ -3,10 +3,10 @@
File::File()
{
hFile=FILE_BAD_HANDLE;
*FileName=0;
NewFile=false;
LastWrite=false;
HandleType=FILE_HANDLENORMAL;
LineInput=false;
SkipClose=false;
ErrorType=FILE_SUCCESS;
OpenShared=false;
@@ -14,11 +14,11 @@ File::File()
AllowExceptions=true;
PreserveAtime=false;
#ifdef _WIN_ALL
// CreateMode=FMF_UNDEFINED;
NoSequentialRead=false;
CreateMode=FMF_UNDEFINED;
#endif
ReadErrorMode=FREM_ASK;
TruncatedAfterReadError=false;
CurFilePos=0;
}
@@ -39,12 +39,12 @@ void File::operator = (File &SrcFile)
LastWrite=SrcFile.LastWrite;
HandleType=SrcFile.HandleType;
TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
FileName=SrcFile.FileName;
wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
SrcFile.SkipClose=true;
}
bool File::Open(const std::wstring &Name,uint Mode)
bool File::Open(const wchar *Name,uint Mode)
{
ErrorType=FILE_SUCCESS;
FileHandle hNewFile;
@@ -58,21 +58,21 @@ bool File::Open(const std::wstring &Name,uint Mode)
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
if (OpenShared)
ShareMode|=FILE_SHARE_WRITE;
uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
uint Flags=NoSequentialRead ? 0: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);
hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
DWORD LastError;
if (hNewFile==FILE_BAD_HANDLE)
{
LastError=GetLastError();
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
{
hNewFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
hNewFile=CreateFile(LongName,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).
@@ -100,12 +100,6 @@ bool File::Open(const std::wstring &Name,uint Mode)
#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)
@@ -117,10 +111,10 @@ bool File::Open(const std::wstring &Name,uint Mode)
if (PreserveAtime)
flags|=O_NOATIME;
#endif
std::string NameA;
WideToChar(Name,NameA);
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
int handle=open(NameA.c_str(),flags);
int handle=open(NameA,flags);
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
@@ -153,7 +147,7 @@ bool File::Open(const std::wstring &Name,uint Mode)
if (Success)
{
hFile=hNewFile;
FileName=Name;
wcsncpyz(FileName,Name,ASIZE(FileName));
TruncatedAfterReadError=false;
}
return Success;
@@ -161,7 +155,7 @@ bool File::Open(const std::wstring &Name,uint Mode)
#if !defined(SFX_MODULE)
void File::TOpen(const std::wstring &Name)
void File::TOpen(const wchar *Name)
{
if (!WOpen(Name))
ErrHandler.Exit(RARX_OPEN);
@@ -169,7 +163,7 @@ void File::TOpen(const std::wstring &Name)
#endif
bool File::WOpen(const std::wstring &Name)
bool File::WOpen(const wchar *Name)
{
if (Open(Name))
return true;
@@ -178,9 +172,8 @@ bool File::WOpen(const std::wstring &Name)
}
bool File::Create(const std::wstring &Name,uint Mode)
bool File::Create(const wchar *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
@@ -188,46 +181,46 @@ bool File::Create(const std::wstring &Name,uint Mode)
bool WriteMode=(Mode & FMF_WRITE)!=0;
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
#ifdef _WIN_ALL
// CreateMode=Mode;
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==' ';
wchar *LastChar=PointToLastChar(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);
hFile=CreateFile(Name,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);
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
}
#else
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
#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);
hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
#else
hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY);
hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
#endif
#endif
NewFile=true;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
FileName=Name;
wcsncpyz(FileName,Name,ASIZE(FileName));
return hFile!=FILE_BAD_HANDLE;
}
#if !defined(SFX_MODULE)
void File::TCreate(const std::wstring &Name,uint Mode)
void File::TCreate(const wchar *Name,uint Mode)
{
if (!WCreate(Name,Mode))
ErrHandler.Exit(RARX_FATAL);
@@ -235,7 +228,7 @@ void File::TCreate(const std::wstring &Name,uint Mode)
#endif
bool File::WCreate(const std::wstring &Name,uint Mode)
bool File::WCreate(const wchar *Name,uint Mode)
{
if (Create(Name,Mode))
return true;
@@ -256,7 +249,7 @@ bool File::Close()
// 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;
Success=CloseHandle(hFile)==TRUE;
#else
#ifdef FILE_USE_OPEN
Success=close(hFile)!=-1;
@@ -286,16 +279,16 @@ bool File::Delete()
}
bool File::Rename(const std::wstring &NewName)
bool File::Rename(const wchar *NewName)
{
// No need to rename if names are already same.
bool Success=(NewName==FileName);
bool Success=wcscmp(FileName,NewName)==0;
if (!Success)
Success=RenameFile(FileName,NewName);
if (Success)
FileName=NewName;
wcsncpyz(FileName,NewName,ASIZE(FileName));
return Success;
}
@@ -333,13 +326,13 @@ bool File::Write(const void *Data,size_t Size)
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;
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
if (!Success)
break;
}
}
else
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)!=FALSE;
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
#else
#ifdef FILE_USE_OPEN
ssize_t Written=write(hFile,Data,Size);
@@ -368,7 +361,7 @@ bool File::Write(const void *Data,size_t Size)
Seek(Tell()-Written,SEEK_SET);
continue;
}
ErrHandler.WriteError(L"",FileName);
ErrHandler.WriteError(NULL,FileName);
}
break;
}
@@ -386,11 +379,10 @@ int File::Read(void *Data,size_t Size)
if (ReadErrorMode==FREM_IGNORE)
FilePos=Tell();
int TotalRead=0;
int ReadSize;
while (true)
{
int ReadSize=DirectRead(Data,Size);
ReadSize=DirectRead(Data,Size);
if (ReadSize==-1)
{
ErrorType=FILE_READERROR;
@@ -404,14 +396,12 @@ int File::Read(void *Data,size_t Size)
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())
if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL)
{
ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
if (Retry)
@@ -425,28 +415,9 @@ int File::Read(void *Data,size_t Size)
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.
return ReadSize; // It can return -1 only if AllowExceptions is disabled.
}
@@ -528,36 +499,6 @@ 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;
@@ -592,8 +533,6 @@ int64 File::Tell()
ErrHandler.SeekError(FileName);
else
return -1;
if (!IsSeekable())
return CurFilePos;
#ifdef _WIN_ALL
LONG HighDist=0;
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
@@ -652,7 +591,7 @@ void File::PutByte(byte Byte)
bool File::Truncate()
{
#ifdef _WIN_ALL
return SetEndOfFile(hFile)!=FALSE;
return SetEndOfFile(hFile)==TRUE;
#else
return ftruncate(GetFD(),(off_t)Tell())==0;
#endif
@@ -678,10 +617,8 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
// 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);
if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
FlushFileBuffers(hFile);
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
@@ -712,15 +649,15 @@ void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
}
void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta)
void File::SetCloseFileTimeByName(const wchar *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);
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
#ifdef UNIX_TIME_NS
timespec times[2];
@@ -728,7 +665,7 @@ void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime
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);
utimensat(AT_FDCWD,NameA,times,0);
#else
utimbuf ut;
if (setm)
@@ -739,47 +676,24 @@ void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime
ut.actime=fta->GetUnix();
else
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
utime(NameA.c_str(),&ut);
utime(NameA,&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)
void File::GetOpenFileTime(RarTime *ft)
{
#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)
FILETIME FileTime;
GetFileTime(hFile,NULL,NULL,&FileTime);
ft->SetWinFT(&FileTime);
#endif
#if defined(_UNIX) || defined(_EMX)
struct stat st;
fstat(GetFD(),&st);
StatToRarTime(st,ftm,ftc,fta);
ft->SetUnix(st.st_mtime);
#endif
}
@@ -810,23 +724,15 @@ bool File::IsDevice()
#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);
Array<byte> Buffer(File::CopyBufferSize());
int64 CopySize=0;
bool CopyAll=(Length==INT64NDF);
while (CopyAll || Length>0)
{
Wait();
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size();
byte *Buf=Buffer.data();
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
byte *Buf=&Buffer[0];
int ReadSize=Read(Buf,SizeToRead);
if (ReadSize==0)
break;

View File

@@ -14,6 +14,8 @@
#define FILE_BAD_HANDLE NULL
#endif
class RAROptions;
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
@@ -41,7 +43,7 @@ enum FILE_MODE_FLAGS {
FMF_STANDARDNAMES=32,
// Mode flags are not defined yet.
// FMF_UNDEFINED=256
FMF_UNDEFINED=256
};
enum FILE_READ_ERROR_MODE {
@@ -57,32 +59,21 @@ class File
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;
bool NoSequentialRead;
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;
wchar FileName[NM];
FILE_ERRORTYPE ErrorType;
public:
@@ -92,15 +83,15 @@ class File
// 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 Open(const wchar *Name,uint Mode=FMF_READ);
void TOpen(const wchar *Name);
bool WOpen(const wchar *Name);
bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
virtual bool Close(); // 'virtual' for MultiFile class.
bool Delete();
bool Rename(const std::wstring &NewName);
bool Rename(const wchar *NewName);
bool Write(const void *Data,size_t Size);
virtual int Read(void *Data,size_t Size);
int DirectRead(void *Data,size_t Size);
@@ -114,17 +105,12 @@ class File
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);
static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
void GetOpenFileTime(RarTime *ft);
virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
virtual int64 FileLength(); // 'virtual' for MultiFile class.
int64 FileLength();
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;}
@@ -133,6 +119,9 @@ class File
int64 Copy(File &Dest,int64 Length=INT64NDF);
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
#ifdef _WIN_ALL
void RemoveSequentialFlag() {NoSequentialRead=true;}
#endif
void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;}
#ifdef _UNIX
@@ -147,9 +136,14 @@ class File
#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;
#ifdef _WIN_ALL
// USB flash performance is poor with 64 KB buffer, 256+ KB resolved it.
// For copying from HDD to same HDD the best performance was with 256 KB
// buffer in XP and with 1 MB buffer in Win10.
return WinNT()==WNT_WXP ? 0x40000:0x100000;
#else
return 0x100000;
#endif
}
};

View File

@@ -1,18 +1,18 @@
#include "rar.hpp"
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
MKDIR_CODE MakeDir(const wchar *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);
wchar *LastChar=PointToLastChar(Name);
bool Special=*LastChar=='.' || *LastChar==' ';
BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
if (RetCode==0 && !FileExist(Name))
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
RetCode=CreateDirectory(LongName.c_str(),NULL);
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
RetCode=CreateDirectory(LongName,NULL);
}
if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
{
@@ -25,10 +25,10 @@ MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
return MKDIR_BADPATH;
return MKDIR_ERROR;
#elif defined(_UNIX)
std::string NameA;
WideToChar(Name,NameA);
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
int ErrCode=mkdir(NameA.c_str(),uattr);
int ErrCode=mkdir(NameA,uattr);
if (ErrCode==-1)
return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
return MKDIR_SUCCESS;
@@ -38,19 +38,12 @@ MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
}
// Simplified version of MakeDir().
bool CreateDir(const std::wstring &Name)
bool CreatePath(const wchar *Path,bool SkipLastName)
{
return MakeDir(Name,false,0)==MKDIR_SUCCESS;
}
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent)
{
if (Path.empty())
if (Path==NULL || *Path==0)
return false;
#ifdef _WIN_ALL
#if defined(_WIN_ALL) || defined(_EMX)
uint DirAttr=0;
#else
uint DirAttr=0777;
@@ -58,36 +51,42 @@ bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent)
bool Success=true;
for (size_t I=0;I<Path.size();I++)
for (const wchar *s=Path;*s!=0;s++)
{
wchar DirName[NM];
if (s-Path>=ASIZE(DirName))
break;
// Process all kinds of path separators, so user can enter Unix style
// path in Windows or Windows in Unix. I>0 check avoids attempting
// path in Windows or Windows in Unix. s>Path check avoids attempting
// creating an empty directory for paths starting from path separator.
if (IsPathDiv(Path[I]) && I>0)
if (IsPathDiv(*s) && s>Path)
{
#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]==':')
if (s==Path+2 && Path[1]==':')
continue;
#endif
std::wstring DirName=Path.substr(0,I);
wcsncpy(DirName,Path,s-Path);
DirName[s-Path]=0;
Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
if (Success && !Silent)
if (Success)
{
mprintf(St(MCreatDir),DirName.c_str());
mprintf(St(MCreatDir),DirName);
mprintf(L" %s",St(MOk));
}
}
}
if (!SkipLastName && !IsPathDiv(GetLastChar(Path)))
if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
return Success;
}
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
{
#if defined(_WIN_ALL)
bool sm=ftm!=NULL && ftm->IsSet();
@@ -99,13 +98,13 @@ void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
if (ResetAttr)
SetFileAttr(Name,0);
HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
HANDLE hFile=CreateFile(Name,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,
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
}
@@ -123,20 +122,18 @@ void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
if (ResetAttr)
SetFileAttr(Name,DirAttr);
#endif
#ifdef _UNIX
#if defined(_UNIX) || defined(_EMX)
File::SetCloseFileTimeByName(Name,ftm,fta);
#endif
}
bool IsRemovable(const std::wstring &Name)
bool IsRemovable(const wchar *Name)
{
#if defined(_WIN_ALL)
std::wstring Root;
GetPathRoot(Name,Root);
int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str());
wchar Root[NM];
GetPathRoot(Name,Root,ASIZE(Root));
int Type=GetDriveType(*Root!=0 ? Root:NULL);
return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
#else
return false;
@@ -145,25 +142,25 @@ bool IsRemovable(const std::wstring &Name)
#ifndef SFX_MODULE
int64 GetFreeDisk(const std::wstring &Name)
int64 GetFreeDisk(const wchar *Name)
{
#ifdef _WIN_ALL
std::wstring Root;
GetPathWithSep(Name,Root);
wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) &&
if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&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);
wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
char RootA[NM];
WideToChar(Root,RootA,ASIZE(RootA));
struct statvfs sfs;
if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0)
if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
return 0;
int64 FreeSize=sfs.f_bsize;
FreeSize=FreeSize*sfs.f_bavail;
@@ -178,27 +175,26 @@ int64 GetFreeDisk(const std::wstring &Name)
#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)
bool IsFAT(const wchar *Name)
{
std::wstring Root;
GetPathRoot(Name,Root);
wchar Root[NM];
GetPathRoot(Name,Root,ASIZE(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)))
if (GetVolumeInformation(Root,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)
bool FileExist(const wchar *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;
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
return access(NameA,0)==0;
#else
FindData FD;
return FindFile::FastFind(Name,&FD);
@@ -206,7 +202,7 @@ bool FileExist(const std::wstring &Name)
}
bool WildFileExist(const std::wstring &Name)
bool WildFileExist(const wchar *Name)
{
if (IsWildcard(Name))
{
@@ -234,9 +230,8 @@ 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
return false;
}
@@ -266,66 +261,70 @@ bool IsDeleteAllowed(uint FileAttr)
}
void PrepareToDelete(const std::wstring &Name)
void PrepareToDelete(const wchar *Name)
{
#ifdef _WIN_ALL
#if defined(_WIN_ALL) || defined(_EMX)
SetFileAttr(Name,0);
#endif
#ifdef _UNIX
std::string NameA;
WideToChar(Name,NameA);
chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR);
if (Name!=NULL)
{
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
}
#endif
}
uint GetFileAttr(const std::wstring &Name)
uint GetFileAttr(const wchar *Name)
{
#ifdef _WIN_ALL
DWORD Attr=GetFileAttributes(Name.c_str());
DWORD Attr=GetFileAttributes(Name);
if (Attr==0xffffffff)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Attr=GetFileAttributes(LongName.c_str());
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Attr=GetFileAttributes(LongName);
}
return Attr;
#else
std::string NameA;
WideToChar(Name,NameA);
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
struct stat st;
if (stat(NameA.c_str(),&st)!=0)
if (stat(NameA,&st)!=0)
return 0;
return st.st_mode;
#endif
}
bool SetFileAttr(const std::wstring &Name,uint Attr)
bool SetFileAttr(const wchar *Name,uint Attr)
{
#ifdef _WIN_ALL
bool Success=SetFileAttributes(Name.c_str(),Attr)!=0;
bool Success=SetFileAttributes(Name,Attr)!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=SetFileAttributes(LongName.c_str(),Attr)!=0;
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Success=SetFileAttributes(LongName,Attr)!=0;
}
return Success;
#elif defined(_UNIX)
std::string NameA;
WideToChar(Name,NameA);
return chmod(NameA.c_str(),(mode_t)Attr)==0;
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
return chmod(NameA,(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)
#if 0
wchar *MkTemp(wchar *Name,size_t MaxSize)
{
size_t Length=wcslen(Name);
RarTime CurTime;
CurTime.SetCurrentTime();
@@ -344,28 +343,18 @@ bool MkTemp(std::wstring &Name,const wchar *Ext)
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;
uint Ext=Random%50000+Attempt;
wchar RndText[50];
swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
return NULL;
wcsncpyz(Name+Length,RndText,MaxSize-Length);
if (!FileExist(Name))
break;
}
}
return true;
return Name;
}
#endif
#if !defined(SFX_MODULE)
@@ -383,7 +372,8 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
SrcFile->Seek(0,SEEK_SET);
const size_t BufSize=0x100000;
std::vector<byte> Data(BufSize);
Array<byte> Data(BufSize);
DataHash HashCRC,HashBlake2;
HashCRC.Init(HASH_CRC32,Threads);
@@ -398,7 +388,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
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);
int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
if (ReadSize==0)
break;
TotalRead+=ReadSize;
@@ -407,11 +397,7 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
{
#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);
}
uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength);
else
{
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
@@ -422,9 +408,9 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
}
if (CRC32!=NULL)
HashCRC.Update(Data.data(),ReadSize);
HashCRC.Update(&Data[0],ReadSize);
if (Blake2!=NULL)
HashBlake2.Update(Data.data(),ReadSize);
HashBlake2.Update(&Data[0],ReadSize);
if (Size!=INT64NDF)
Size-=ReadSize;
@@ -446,109 +432,73 @@ void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,
#endif
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName)
bool RenameFile(const wchar *SrcName,const wchar *DestName)
{
#ifdef _WIN_ALL
bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0;
bool Success=MoveFile(SrcName,DestName)!=0;
if (!Success)
{
std::wstring LongName1,LongName2;
if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2))
Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0;
wchar LongName1[NM],LongName2[NM];
if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
Success=MoveFile(LongName1,LongName2)!=0;
}
return Success;
#else
std::string SrcNameA,DestNameA;
WideToChar(SrcName,SrcNameA);
WideToChar(DestName,DestNameA);
bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0;
char SrcNameA[NM],DestNameA[NM];
WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
WideToChar(DestName,DestNameA,ASIZE(DestNameA));
bool Success=rename(SrcNameA,DestNameA)==0;
return Success;
#endif
}
bool DelFile(const std::wstring &Name)
bool DelFile(const wchar *Name)
{
#ifdef _WIN_ALL
bool Success=DeleteFile(Name.c_str())!=0;
bool Success=DeleteFile(Name)!=0;
if (!Success)
{
std::wstring LongName;
if (GetWinLongPath(Name,LongName))
Success=DeleteFile(LongName.c_str())!=0;
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
Success=DeleteFile(LongName)!=0;
}
return Success;
#else
std::string NameA;
WideToChar(Name,NameA);
bool Success=remove(NameA.c_str())==0;
char NameA[NM];
WideToChar(Name,NameA,ASIZE(NameA));
bool Success=remove(NameA)==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)
bool SetFileCompression(const wchar *Name,bool State)
{
HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
HANDLE hFile=CreateFile(Name,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,
wchar LongName[NM];
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
hFile=CreateFile(LongName,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)
{
if (hFile==INVALID_HANDLE_VALUE)
return false;
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);
CloseHandle(hFile);
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
@@ -559,73 +509,3 @@ void ResetFileCache(const std::wstring &Name)
// 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;
}

View File

@@ -3,55 +3,48 @@
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);
MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
bool CreatePath(const wchar *Path,bool SkipLastName);
void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
bool IsRemovable(const wchar *Name);
#ifndef SFX_MODULE
int64 GetFreeDisk(const std::wstring &Name);
int64 GetFreeDisk(const wchar *Name);
#endif
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool IsFAT(const std::wstring &Root);
bool IsFAT(const wchar *Root);
#endif
bool FileExist(const std::wstring &Name);
bool WildFileExist(const std::wstring &Name);
bool FileExist(const wchar *Name);
bool WildFileExist(const wchar *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);
void SetSFXMode(const wchar *FileName);
void EraseDiskContents(const wchar *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);
void PrepareToDelete(const wchar *Name);
uint GetFileAttr(const wchar *Name);
bool SetFileAttr(const wchar *Name,uint Attr);
#if 0
wchar* MkTemp(wchar *Name,size_t MaxSize);
#endif
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);
bool RenameFile(const wchar *SrcName,const wchar *DestName);
bool DelFile(const wchar *Name);
bool DelDir(const wchar *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);
bool SetFileCompression(const wchar *Name,bool State);
#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

View File

@@ -1,7 +1,7 @@
#include "rar.hpp"
bool ReadTextFile(
const std::wstring &Name,
const wchar *Name,
StringList *List,
bool Config,
bool AbortOnError,
@@ -10,15 +10,17 @@ bool ReadTextFile(
bool SkipComments,
bool ExpandEnvStr)
{
std::wstring FileName;
wchar FileName[NM];
*FileName=0;
if (Config)
GetConfigName(Name,FileName,true,false);
else
FileName=Name;
if (Name!=NULL)
if (Config)
GetConfigName(Name,FileName,ASIZE(FileName),true,false);
else
wcsncpyz(FileName,Name,ASIZE(FileName));
File SrcFile;
if (!FileName.empty())
if (*FileName!=0)
{
bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
@@ -32,36 +34,36 @@ bool ReadTextFile(
else
SrcFile.SetHandleType(FILE_HANDLESTD);
size_t DataSize=0,ReadSize;
uint DataSize=0,ReadSize;
const int ReadBlock=4096;
std::vector<byte> Data(ReadBlock);
Array<byte> Data(ReadBlock);
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
{
DataSize+=ReadSize;
Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data.
Data.Add(ReadSize); // Always have ReadBlock available for next data.
}
// Set to really read size, so we can zero terminate it correctly.
Data.resize(DataSize);
Data.Alloc(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);
SrcCharset=DetectTextEncoding(&Data[0],DataSize);
std::vector<wchar> DataW(ReadBlock);
Array<wchar> DataW;
if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
{
Data.push_back(0); // Zero terminate.
Data.Push(0); // Zero terminate.
#if defined(_WIN_ALL)
if (SrcCharset==RCH_OEM)
OemToCharA((char *)Data.data(),(char *)Data.data());
OemToCharA((char *)&Data[0],(char *)&Data[0]);
#endif
DataW.resize(Data.size());
CharToWide((char *)Data.data(),DataW.data(),DataW.size());
DataW.Alloc(Data.Size());
CharToWide((char *)&Data[0],&DataW[0],DataW.Size());
}
if (SrcCharset==RCH_UNICODE)
@@ -73,8 +75,8 @@ bool ReadTextFile(
LittleEndian=1;
}
DataW.resize(Data.size()/2+1);
size_t End=Data.size() & ~1; // We need even bytes number for UTF-16.
DataW.Alloc(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;
@@ -82,12 +84,12 @@ bool ReadTextFile(
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());
Data.Push(0); // Zero terminate data.
DataW.Alloc(Data.Size());
UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size());
}
wchar *CurStr=DataW.data();
wchar *CurStr=&DataW[0];
while (*CurStr!=0)
{
@@ -125,11 +127,12 @@ bool ReadTextFile(
#if defined(_WIN_ALL)
if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
{
std::wstring ExpName=CurStr;
ExpandEnvironmentStr(ExpName);
if (!ExpName.empty())
wchar ExpName[NM];
*ExpName=0;
DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
Expanded=Result!=0 && Result<ASIZE(ExpName);
if (Expanded && *ExpName!=0)
List->AddString(ExpName);
Expanded=true;
}
#endif
if (!Expanded && *CurStr!=0)

View File

@@ -2,7 +2,7 @@
#define _RAR_FILESTR_
bool ReadTextFile(
const std::wstring &Name,
const wchar *Name,
StringList *List,
bool Config,
bool AbortOnError=false,

View File

@@ -2,6 +2,7 @@
FindFile::FindFile()
{
*FindMask=0;
FirstCall=true;
#ifdef _WIN_ALL
hFind=INVALID_HANDLE_VALUE;
@@ -23,9 +24,9 @@ FindFile::~FindFile()
}
void FindFile::SetMask(const std::wstring &Mask)
void FindFile::SetMask(const wchar *Mask)
{
FindMask=Mask;
wcsncpyz(FindMask,Mask,ASIZE(FindMask));
FirstCall=true;
}
@@ -33,7 +34,7 @@ void FindFile::SetMask(const std::wstring &Mask)
bool FindFile::Next(FindData *fd,bool GetSymLink)
{
fd->Error=false;
if (FindMask.empty())
if (*FindMask==0)
return false;
#ifdef _WIN_ALL
if (FirstCall)
@@ -47,14 +48,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
#else
if (FirstCall)
{
std::wstring DirName;
DirName=FindMask;
wchar DirName[NM];
wcsncpyz(DirName,FindMask,ASIZE(DirName));
RemoveNameFromPath(DirName);
if (DirName.empty())
DirName=L".";
std::string DirNameA;
WideToChar(DirName,DirNameA);
if ((dirp=opendir(DirNameA.c_str()))==NULL)
if (*DirName==0)
wcsncpyz(DirName,L".",ASIZE(DirName));
char DirNameA[NM];
WideToChar(DirName,DirNameA,ASIZE(DirNameA));
if ((dirp=opendir(DirNameA))==NULL)
{
fd->Error=(errno!=ENOENT);
return false;
@@ -62,31 +63,32 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
}
while (1)
{
std::wstring Name;
wchar Name[NM];
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 (!CharToWide(ent->d_name,Name,ASIZE(Name)))
uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
if (CmpName(FindMask,Name,MATCH_NAMES))
{
std::wstring FullName=FindMask;
FullName.erase(GetNamePos(FullName));
if (FullName.size()+Name.size()>=MAXPATHSIZE)
wchar FullName[NM];
wcsncpyz(FullName,FindMask,ASIZE(FullName));
*PointToName(FullName)=0;
if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
{
uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
return false;
}
FullName+=Name;
wcsncatz(FullName,Name,ASIZE(FullName));
if (!FastFind(FullName,fd,GetSymLink))
{
ErrHandler.OpenErrorMsg(FullName);
continue;
}
fd->Name=FullName;
wcsncpyz(fd->Name,FullName,ASIZE(fd->Name));
break;
}
}
@@ -96,14 +98,14 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
fd->IsLink=IsLink(fd->FileAttr);
FirstCall=false;
std::wstring NameOnly=PointToName(fd->Name);
if (NameOnly==L"." || NameOnly==L"..")
wchar *NameOnly=PointToName(fd->Name);
if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
return Next(fd);
return true;
}
bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink)
bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
{
fd->Error=false;
#ifndef _UNIX
@@ -115,17 +117,17 @@ bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLin
if (hFind==INVALID_HANDLE_VALUE)
return false;
FindClose(hFind);
#elif defined(_UNIX)
std::string FindMaskA;
WideToChar(FindMask,FindMaskA);
#else
char FindMaskA[NM];
WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
struct stat st;
if (GetSymLink)
{
#ifdef SAVE_LINKS
if (lstat(FindMaskA.c_str(),&st)!=0)
if (lstat(FindMaskA,&st)!=0)
#else
if (stat(FindMaskA.c_str(),&st)!=0)
if (stat(FindMaskA,&st)!=0)
#endif
{
fd->Error=(errno!=ENOENT);
@@ -133,7 +135,7 @@ bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLin
}
}
else
if (stat(FindMaskA.c_str(),&st)!=0)
if (stat(FindMaskA,&st)!=0)
{
fd->Error=(errno!=ENOENT);
return false;
@@ -141,9 +143,17 @@ bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLin
fd->FileAttr=st.st_mode;
fd->Size=st.st_size;
File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
#ifdef UNIX_TIME_NS
fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
#else
fd->mtime.SetUnix(st.st_mtime);
fd->atime.SetUnix(st.st_atime);
fd->ctime.SetUnix(st.st_ctime);
#endif
fd->Name=FindMask;
wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
@@ -154,17 +164,17 @@ bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLin
#ifdef _WIN_ALL
HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd)
HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
{
WIN32_FIND_DATA FindData;
if (hFind==INVALID_HANDLE_VALUE)
{
hFind=FindFirstFile(Mask.c_str(),&FindData);
hFind=FindFirstFile(Mask,&FindData);
if (hFind==INVALID_HANDLE_VALUE)
{
std::wstring LongMask;
if (GetWinLongPath(Mask,LongMask))
hFind=FindFirstFile(LongMask.c_str(),&FindData);
wchar LongMask[NM];
if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
hFind=FindFirstFile(LongMask,&FindData);
}
if (hFind==INVALID_HANDLE_VALUE)
{
@@ -188,8 +198,8 @@ HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd)
if (hFind!=INVALID_HANDLE_VALUE)
{
fd->Name=Mask;
SetName(fd->Name,FindData.cFileName);
wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
fd->FileAttr=FindData.dwFileAttributes;
fd->ftCreationTime=FindData.ftCreationTime;

View File

@@ -7,7 +7,7 @@ enum FINDDATA_FLAGS {
struct FindData
{
std::wstring Name;
wchar Name[NM];
uint64 Size;
uint FileAttr;
bool IsDir;
@@ -28,10 +28,10 @@ class FindFile
{
private:
#ifdef _WIN_ALL
static HANDLE Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd);
static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
#endif
std::wstring FindMask;
wchar FindMask[NM];
bool FirstCall;
#ifdef _WIN_ALL
HANDLE hFind;
@@ -41,9 +41,9 @@ class FindFile
public:
FindFile();
~FindFile();
void SetMask(const std::wstring &Mask);
void SetMask(const wchar *Mask);
bool Next(FindData *fd,bool GetSymLink=false);
static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false);
static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
};
#endif

View File

@@ -5,11 +5,11 @@ 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
// getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
// So let's allocate 3 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;
// from access to next 3 bytes, which contents we do not need.
size_t BufSize=MAX_SIZE+3;
InBuf=new byte[BufSize];
// Ensure that we get predictable results when accessing bytes in area
@@ -17,7 +17,7 @@ BitInput::BitInput(bool AllocBuffer)
memset(InBuf,0,BufSize);
}
else
InBuf=nullptr;
InBuf=NULL;
}
@@ -30,21 +30,21 @@ BitInput::~BitInput()
void BitInput::faddbits(uint Bits)
{
// Function wrapped version of inline addbits to reduce the code size.
// Function wrapped version of inline addbits to save code size.
addbits(Bits);
}
uint BitInput::fgetbits()
{
// Function wrapped version of inline getbits to reduce the code size.
// Function wrapped version of inline getbits to save code size.
return getbits();
}
void BitInput::SetExternalBuffer(byte *Buf)
{
if (InBuf!=nullptr && !ExternalBuffer)
if (InBuf!=NULL && !ExternalBuffer)
delete[] InBuf;
InBuf=Buf;
ExternalBuffer=true;

View File

@@ -28,43 +28,30 @@ class BitInput
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);
uint BitField=(uint)InBuf[InAddr] << 24;
BitField|=(uint)InBuf[InAddr+1] << 16;
BitField|=(uint)InBuf[InAddr+2] << 8;
BitField|=(uint)InBuf[InAddr+3];
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();

View File

@@ -1,6 +1,6 @@
#define INCLUDEGLOBAL
#ifdef _MSC_VER
#if defined(__BORLANDC__) || defined(_MSC_VER)
#pragma hdrstop
#endif

Some files were not shown because too many files have changed in this diff Show More