mirror of
https://github.com/LisherSong/ps5-web-file-manager.git
synced 2026-10-06 09:00:26 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c4eadbf5 | ||
|
|
ad765776f7 | ||
|
|
ae41434089 | ||
|
|
af6b440f33 | ||
|
|
01a6616472 | ||
|
|
068983b062 | ||
|
|
da67bfa284 | ||
|
|
452b9b0187 | ||
|
|
91757bfd02 | ||
|
|
30d0decf58 | ||
|
|
22a4b5c76a | ||
|
|
ff010add68 | ||
|
|
68056f2d9f | ||
|
|
3db921e408 | ||
|
|
c58c145733 | ||
|
|
a0482a41ed | ||
|
|
c68c0def35 | ||
|
|
8344b9bae0 | ||
|
|
cb82ee439d | ||
|
|
9e9830a214 | ||
|
|
687ef6b297 | ||
|
|
36ea055e70 | ||
|
|
2a346d694c | ||
|
|
bf55a4e4fd |
No files matched your search
+13
-3
@@ -148,10 +148,14 @@ echo "[6/7] make all (增量编译, 复用第三方 obj)..."
|
||||
cd "$PROJ"
|
||||
export PS5_PAYLOAD_SDK="/opt/ps5-payload-sdk"
|
||||
# 仅当二进制缺失或源码变更才全量重编
|
||||
# 重要: 必须包含 assets/* 和 gen-asset-module.py —— 它们经 gen-asset-module.py
|
||||
# 生成 gen/*.c 进而影响 ELF, 不在列表里就会跳过 make 产生伪"无变更"(v1.8.3
|
||||
# 被这个 bug 坑过, ELF sha256 没变)。
|
||||
if [ -f web-file-mgr.elf ]; then
|
||||
echo " 已存在 web-file-mgr.elf, 检查源码变更..."
|
||||
NEEDS_REBUILD=""
|
||||
for src in src/*.c Makefile third_party/minizip-ng/include/*.h third_party/zlib/include/*.h; do
|
||||
for src in src/*.c Makefile assets/* gen-asset-module.py \
|
||||
third_party/minizip-ng/include/*.h third_party/zlib/include/*.h; do
|
||||
[ -e "$src" ] || continue
|
||||
if [ "$src" -nt web-file-mgr.elf ]; then
|
||||
NEEDS_REBUILD="$src"
|
||||
@@ -162,10 +166,16 @@ if [ -f web-file-mgr.elf ]; then
|
||||
echo " 无源码变更 -- 跳过 make"
|
||||
else
|
||||
echo " 源码变更: $NEEDS_REBUILD"
|
||||
make all 2>&1 | tail -40
|
||||
if ! make all 2>&1 | tail -60; then
|
||||
echo "FAIL: make all 失败(见上)。注意: 旧 ELF 仍留在原地, 但不算新产物"
|
||||
exit 6
|
||||
fi
|
||||
fi
|
||||
else
|
||||
make all 2>&1 | tail -40
|
||||
if ! make all 2>&1 | tail -60; then
|
||||
echo "FAIL: make all 失败(见上)"
|
||||
exit 6
|
||||
fi
|
||||
fi
|
||||
|
||||
# 7. 验证 + 同步回 Windows
|
||||
|
||||
+280
-6
@@ -4,14 +4,288 @@ All notable changes to **PS5 Web File Manager** are documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
> Release artifact for v1.8:
|
||||
> `web-file-mgr.elf` — size TBD (cross-compile runs in WSL — see `docs/HANDOVER.md`)
|
||||
> sha256 TBD
|
||||
> Release artifact for v1.9:
|
||||
> `web-file-mgr.elf` — size 919 440 bytes (~897 KiB)
|
||||
> sha256 `bb8f17e9addc6a9984f611503ca01b51f8984b773d353630da1f25bd1a28a997`
|
||||
> ELF class 64, little-endian, e_machine `0x003e` (x86_64-sie-ps5)
|
||||
>
|
||||
> Source delta vs v1.7: +2 vendored files (`third_party/unrar/dmc_unrar.c`,
|
||||
> `third_party/unrar/dmc_unrar_api.h`), +1 new source pair
|
||||
> (`src/rar_extract.{c,h}`), `src/extract.c` gains a dispatch layer.
|
||||
> Source delta vs v1.8.3: RAR engine replaced (dmc_unrar 1.7.0 → rarlab
|
||||
> UnRAR 7.20.1, `third_party/unrar/` → `third_party/unrar7/`), new
|
||||
> `src/rar_extract.c` scan/extract implementation, Makefile + host-test
|
||||
> C++ rules, 5 real RAR fixtures committed. See [v1.9] below.
|
||||
>
|
||||
> Release artifact for v1.8.3:
|
||||
> `web-file-mgr.elf` — size 509 704 bytes (~497 KiB)
|
||||
> sha256 `fdcf7b09b69e2160e77dfa084c0e890ba0696d4dd478b1d5ff499cdc9f527955`
|
||||
> ELF class 64, little-endian, e_machine `0x003e` (x86_64-sie-ps5)
|
||||
>
|
||||
> Source delta vs v1.8.2: 5 files touched (4 user-facing + 1 build pipeline) —
|
||||
> see [v1.8.3] below for details.
|
||||
>
|
||||
> Release artifact for v1.8.2:
|
||||
> `web-file-mgr.elf` — size 509 704 bytes (~497 KiB)
|
||||
> sha256 `1b2c3d68b35e32737105f17d14a80a3c159ceca0cabd274ee168cbcd81906f65`
|
||||
> ELF class 64, little-endian, e_machine `0x003e` (x86_64-sie-ps5)
|
||||
>
|
||||
> Source delta vs v1.8.1: 5 files relaxed (`src/zip_extract.c` k_default_limits
|
||||
> + k_large_limits, `assets/main.js` `LARGE_FILE_THRESHOLD_BYTES`,
|
||||
> `assets/lang-{en,zh}.js` copy, `tests/test_zip_extract.c` advertised-number
|
||||
> assertion, README/HANDOVER numeric references) + 2 PS5-only build fixes
|
||||
> (`Makefile` CFLAGS `-Ithird_party/unrar`, `src/extract.c` forward
|
||||
> declaration of `extract_progress`); no vendored or engine changes.
|
||||
|
||||
## [v1.9] — 2026-09-05
|
||||
|
||||
**RAR engine replaced: rarlab UnRAR 7.20.1 (v6 / multi-volume / decryption-capable).**
|
||||
|
||||
The vendored dmc_unrar 1.7.0 only dispatched RAR5 compression version 5
|
||||
(`switch(file->version)` case `0x5000`). Archives written by WinRAR 6.x /
|
||||
7.x (algorithm string `v6`, version field `0x5001`) hit the default branch
|
||||
and surfaced as "corrupt archive" — confirmed on a real `v6:8M` archive.
|
||||
v1.9 swaps in the official rarlab UnRAR source (7.20.1) via its
|
||||
C-compatible DLL API, compiled as a static library (`-DRARDLL`, PS5 uses
|
||||
the toolchain's default `libc++`).
|
||||
|
||||
What this enables:
|
||||
|
||||
- **RAR5 "v6" compression** (WinRAR 6/7 archives) — the v1.9 trigger.
|
||||
- **Multi-volume RAR** (`.partNN.rar`): unrar stitches volumes by name when
|
||||
all parts sit next to the opened volume. Select the first volume
|
||||
(`name.part1.rar`) and extract as usual.
|
||||
- RAR4 and older RAR5 remain supported.
|
||||
- The engine *can* decrypt encrypted archives (`RARSetPassword`), but the
|
||||
password channel (API + UI) is not wired yet — encrypted headers/entries
|
||||
still fail up front with `err_extract_unsupported`. Planned for a follow-up.
|
||||
|
||||
Engine changes:
|
||||
|
||||
- `src/rar_extract.c` rewritten to unrar's sequential DLL API
|
||||
(`RAROpenArchiveEx → RARReadHeaderEx → RARProcessFile`); scan and extract
|
||||
each re-open the archive. Multi-volume continuation segments
|
||||
(`RHDF_SPLITBEFORE`) are advanced but not re-counted/deduped.
|
||||
- The three-phase scan → staging → publish/rollback machinery is unchanged.
|
||||
- Bug fix: `normalize_name()` no longer clears the caller's directory flag
|
||||
(a real v6 archive with an explicit directory header after its files
|
||||
tripped the duplicate detector).
|
||||
|
||||
Build & test:
|
||||
|
||||
- Makefile: `.cpp` rules for the unrar RARDLL source set (49 files, mirrors
|
||||
`UnRARDll.vcxproj`); links through the C++ driver; `VERSION_TAG` v1.9.
|
||||
- tests: 5 real RAR fixtures committed under `tests/fixtures-real/`
|
||||
(generated with `tests/make-rar-fixtures.bat` + WinRAR); new happy-path
|
||||
checks extract a real v6 archive, verify files on disk, auto-merge a
|
||||
3-volume split, and reject encrypted archives. Total: **70 ZIP + 24 RAR
|
||||
= 94 checks** (up from 70 + 14; the old 14 RAR checks never ran a real
|
||||
archive).
|
||||
|
||||
Credits: unrar (c) Alexander Roshal, freeware license — see
|
||||
`third_party/unrar7/license.txt` and `THIRD_PARTY_NOTICES`.
|
||||
|
||||
## [v1.8.3] — 2026-09-05
|
||||
|
||||
**Hotfix: "Upload and extract" now accepts `.rar` files.**
|
||||
|
||||
The "upload and extract" entry was hard-coded to accept only `.zip`,
|
||||
even though the server-side dispatch in `src/extract.c:79` already
|
||||
correctly routes `.rar` to `rar_extract()`. v1.8.3 fixes the frontend
|
||||
filter so users can select a single-volume plaintext `.rar` from the
|
||||
file picker and have it uploaded + extracted in one click (the same
|
||||
flow that already worked for `.zip`).
|
||||
|
||||
What this enables:
|
||||
|
||||
- Choose a single-volume `.rar` from "Upload and extract"
|
||||
- Server extracts it via the existing `rar_extract()` engine
|
||||
- Uploaded `.rar` is auto-deleted after a successful extract (same as
|
||||
`.zip` since v1.7)
|
||||
|
||||
What this does **not** enable (planned for v1.9.0):
|
||||
|
||||
- **Multi-volume RAR** (e.g. `name.part01.rar` + `name.part02.rar` …)
|
||||
- **Encrypted RAR** (password-protected headers or entries)
|
||||
|
||||
Both still return `extract_unsupported` "single-volume RAR only" /
|
||||
"encrypted RAR is not supported; please extract on a PC first" — see
|
||||
the underlying engine limit in `third_party/unrar/dmc_unrar` (GPL-2.0,
|
||||
1.7.0). v1.9 will swap the vendor to **opello/unrar 7.20.1** (UnRAR
|
||||
License) which natively supports both.
|
||||
|
||||
Changed:
|
||||
|
||||
- `assets/index.html` — `<input id="uploadZip" accept>` now lists
|
||||
`.rar` + the two RAR MIME types next to the existing ZIP entries.
|
||||
- `assets/main.js:2340` — `/\.zip$/i` → `/\.(zip|rar)$/i` (the upload
|
||||
pre-check), plus a local `isRar` flag so the next step branches.
|
||||
- `assets/lang-en.js` — `extractUploadConfirm`: "uploaded ZIP" →
|
||||
"uploaded archive".
|
||||
- `assets/lang-zh.js` — `extractUploadConfirm` & `extractLargeAsk`
|
||||
drop the "ZIP" wording so the copy reads sensibly for RAR uploads.
|
||||
- `assets/main.js:38` — `APP_VERSION` `"v1.7"` → `"v1.8.3"` (footer
|
||||
version string had been hard-coded to v1.7 since the frontend was
|
||||
first imported; it no longer misleads about which build is running).
|
||||
|
||||
No backend changes — the server side was already correct. No test
|
||||
changes — the existing RAR happy-path test in `tests/test_rar_extract.c`
|
||||
passes against the same backend.
|
||||
|
||||
Build pipeline (also v1.8.3):
|
||||
|
||||
- `.build/build-elf.sh` step 6 "no source change → skip make" check now
|
||||
also watches `assets/*` and `gen-asset-module.py`, not just `src/*.c`
|
||||
and the `Makefile`. Without this, v1.8.3 (which touched no backend,
|
||||
only frontend assets feeding `gen/*.c`) was misclassified as "no
|
||||
change" and `make` was skipped — the result was that the v1.8.2 ELF
|
||||
was reported as v1.8.3 with the same sha256. With this fix, only
|
||||
frontend changes correctly trigger a rebuild. Users running the WSL
|
||||
build need to re-copy `.build/build-elf.sh` to `/home/song/build-elf.sh`
|
||||
(canonical source is on the Windows side).
|
||||
|
||||
## [v1.8.2] — 2026-09-05
|
||||
|
||||
**Hotfix: default ZIP extraction limits cover 3A-game single-file archives.**
|
||||
|
||||
The default `k_default_limits` profile is now **2 TiB total / 512 GiB per
|
||||
entry / 500 : 1 ratio** (was 1 TiB / 256 GiB / 500 : 1 in v1.8.1). The
|
||||
frontend threshold `LARGE_FILE_THRESHOLD_BYTES` is bumped from 240 GiB to
|
||||
**480 GiB** to match. The `large=1` profile is widened to **4 TiB total
|
||||
/ 1 TiB per entry / 1000 : 1 ratio** (was 2 TiB / 1 TiB / 1000 : 1); the
|
||||
large profile must always be strictly more permissive than default.
|
||||
|
||||
Why: a 3A-game archive with a single ~300 GiB uncompressed file was
|
||||
**silently rejected by the default profile** (`scan_archive` returns
|
||||
`ZIPX_ERR_LIMIT_FILE_SIZE` in `src/zip_extract.c` line ~717 — the request
|
||||
never reaches the frontend confirmation prompt, so the user just sees
|
||||
"卡壳"). The 256 GiB default cap was tuned for PS5 system backups (which
|
||||
have many smaller entries, not a single huge file) and was wrong for the
|
||||
3A-game single-file case. The default cap is now 512 GiB so a typical
|
||||
3A archive extracts under the default profile without prompting.
|
||||
|
||||
The safety argument is unchanged from v1.8.1: zip-bomb defence is
|
||||
`check_space()` (`statvfs`-based real disk space check before staging) +
|
||||
`max_ratio` (declared compression ratio cap). The size caps are a UX
|
||||
guard, not a security boundary.
|
||||
|
||||
RAR extraction inherits the new defaults automatically — rar_extract.c
|
||||
threads `c->limits` through from the engine, so no rar-side change is
|
||||
required.
|
||||
|
||||
### Changed
|
||||
|
||||
- `src/zip_extract.c` — `k_default_limits` relaxed:
|
||||
- `max_total_bytes`: 1 TiB → **2 TiB**
|
||||
- `max_file_bytes`: 256 GiB → **512 GiB**
|
||||
- `max_ratio`: 500 → **500** (unchanged)
|
||||
- `src/zip_extract.c` — `k_large_limits` widened (must stay > default):
|
||||
- `max_total_bytes`: 2 TiB → **4 TiB**
|
||||
- `max_file_bytes`: 1 TiB → **1 TiB** (unchanged)
|
||||
- `max_ratio`: 1000 → **1000** (unchanged)
|
||||
- `assets/main.js` — `LARGE_FILE_THRESHOLD_BYTES`: 240 GiB → **480 GiB**
|
||||
- `assets/lang-{en,zh}.js` — `extractLargeAsk` copy updated to reflect
|
||||
the new numbers (default 512 GiB / 2 TiB; large 1 TiB / 4 TiB)
|
||||
- `tests/test_zip_extract.c` — `test_large_profile` advertised-number
|
||||
assertion updated: `max_total_bytes == 4 TiB` (was 2 TiB)
|
||||
- `README.md` — both limit tables (ZIP + RAR), the "Tuning the threshold"
|
||||
snippet, and the `err_extract_entry_too_large` FAQ entry bumped to the
|
||||
new numbers
|
||||
- `docs/HANDOVER.md` — `LARGE_FILE_THRESHOLD_BYTES`, the
|
||||
`k_default_limits` / `k_large_limits` ASCII diagram, the user-scenario
|
||||
description, the 480 GiB popup note, and the RAR limits paragraph
|
||||
bumped to the new numbers
|
||||
|
||||
### Unchanged
|
||||
|
||||
- `src/rar_extract.c` — already threads `c->limits` from the engine,
|
||||
picks up the new defaults for free
|
||||
- `max_ratio` — both profiles unchanged (500 : 1 default / 1000 : 1 large)
|
||||
- `check_space()` — unchanged; still the real disk-space guard
|
||||
- `max_entries` — 200 000 default / 500 000 large, unchanged
|
||||
- `docs/UPGRADE-v1.7-zip-large-file-profile.md` — historical v1.7
|
||||
document left as-is so the v1.7 → v1.8.2 evolution is traceable
|
||||
- Test fixture `medium_bomb.zip` (ratio ≈ 238) still exercises both
|
||||
rejection under the default 500 : 1 cap and acceptance under the
|
||||
`large=1` 1000 : 1 cap
|
||||
- 84 host-side checks (70 ZIP + 14 RAR), 0 failures
|
||||
|
||||
### Migration notes
|
||||
|
||||
- **Forward-compatible** — users with v1.8.1 deployments who never trigger
|
||||
`err_extract_entry_too_large` see no difference (defaults are strictly
|
||||
more permissive)
|
||||
- **3A-game single-file archives now extract silently** — no prompt, no
|
||||
manual `large=1` API call required for files up to 512 GiB
|
||||
- **No data loss** — the relaxation only widens accepted archives; the
|
||||
real security guards (`check_space`, `max_ratio`, `path traversal`)
|
||||
are untouched
|
||||
- **No frontend UX change for typical use** — only archives > 480 GiB
|
||||
on disk now trigger the confirmation prompt (previously 240 GiB)
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.1] — 2026-09-05
|
||||
|
||||
**Hotfix: relaxed default ZIP extraction limits.**
|
||||
|
||||
The default `k_default_limits` profile is now **1 TiB total / 256 GiB per
|
||||
entry / 500 : 1 ratio** (was 512 GiB / 64 GiB / 200 : 1). The frontend
|
||||
threshold `LARGE_FILE_THRESHOLD_BYTES` is bumped from 60 GiB to 240 GiB
|
||||
to match. The `large=1` profile (1 TiB / 1 TiB / 1000 : 1) is unchanged.
|
||||
|
||||
Why: the previous default was a UX-oriented early-fail guard, not a
|
||||
security guard — `check_space()` already enforces available ≥ bytes_total
|
||||
before staging begins, and `max_ratio` already rejects classic zip
|
||||
bombs. A user with a multi-hundred-GiB system image shouldn't have to
|
||||
click through a confirmation prompt for what's a perfectly safe archive.
|
||||
The relaxed default still rejects any archive whose declared
|
||||
uncompressed total exceeds the destination's free space (real check,
|
||||
not a declared-vs-fs assertion) and any archive with a declared ratio
|
||||
above 500 : 1 (real zip-bomb guard).
|
||||
|
||||
RAR extraction inherits the new defaults automatically — rar_extract.c
|
||||
threads `c->limits` through from the engine, so no rar-side change is
|
||||
required.
|
||||
|
||||
### Changed
|
||||
|
||||
- `src/zip_extract.c` — `k_default_limits` relaxed:
|
||||
- `max_total_bytes`: 512 GiB → **1 TiB**
|
||||
- `max_file_bytes`: 64 GiB → **256 GiB**
|
||||
- `max_ratio`: 200 → **500**
|
||||
- `assets/main.js` — `LARGE_FILE_THRESHOLD_BYTES`: 60 GiB → **240 GiB**
|
||||
- `assets/lang-{en,zh}.js` — `extractLargeAsk` default-profile copy
|
||||
updated to reflect the new numbers
|
||||
- `README.md` — "Stricter default ZIP profile" line, the limit table
|
||||
(two locations), and the `err_extract_entry_too_large` FAQ entry
|
||||
bumped to the new numbers; "Tuning the threshold" snippet updated to
|
||||
240 GiB
|
||||
- `docs/HANDOVER.md` and `docs/UPGRADE-v1.8-rar-support.md` — the
|
||||
few remaining numeric references in those docs updated
|
||||
|
||||
### Unchanged
|
||||
|
||||
- `src/rar_extract.c` — already threads `c->limits` from the engine,
|
||||
picks up the new defaults for free
|
||||
- `k_large_limits` — `large=1` profile (1 TiB / 1 TiB / 1000 : 1) is
|
||||
unchanged
|
||||
- `docs/UPGRADE-v1.7-zip-large-file-profile.md` — historical v1.7
|
||||
document left as-is so the v1.7 → v1.8.1 evolution is traceable
|
||||
- Test fixture `medium_bomb.zip` (ratio ≈ 238) still exercises both
|
||||
rejection under the default 500 : 1 cap and acceptance under the
|
||||
`large=1` 1000 : 1 cap
|
||||
- 83 host-side checks (69 ZIP + 14 RAR), 0 failures
|
||||
|
||||
### Migration notes
|
||||
|
||||
- **Forward-compatible** — users with v1.7 / v1.8 deployments who never
|
||||
trigger `err_extract_entry_too_large` see no difference (defaults are
|
||||
strictly more permissive)
|
||||
- **No data loss** — the relaxation only widens accepted archives; the
|
||||
real security guards (`check_space`, `max_ratio`, `path traversal`)
|
||||
are untouched
|
||||
- **No frontend UX change for typical use** — only archives > 240 GiB
|
||||
on disk now trigger the confirmation prompt (previously 60 GiB)
|
||||
|
||||
---
|
||||
|
||||
## [v1.8] — 2026-09-05
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ ifeq ($(MAKECMDGOALS),)
|
||||
endif
|
||||
endif
|
||||
|
||||
VERSION_TAG := v1.8
|
||||
VERSION_TAG := v1.9
|
||||
TITLE_ID := FMGR88888
|
||||
PYTHON ?= python3
|
||||
STRIP ?= $(PS5_PAYLOAD_SDK)/bin/prospero-strip
|
||||
@@ -25,7 +25,7 @@ HOST_PKG_CONFIG ?= pkg-config
|
||||
BIN := web-file-mgr.elf
|
||||
LINUX_BIN := web-file-mgr-linux
|
||||
COMMON_SRCS := src/main.c src/websrv.c src/filemgr.c src/file_response.c src/task.c src/upload.c src/download.c src/text.c src/list.c src/space.c src/fs_util.c src/json_util.c src/path_util.c src/asset.c src/mime.c src/notify.c src/pkg_installer.c src/pkg_info.c src/extract.c src/zip_extract.c src/rar_extract.c
|
||||
PS5_SRCS := $(COMMON_SRCS) src/app_installer.c
|
||||
PS5_SRCS := $(COMMON_SRCS) src/app_installer.c src/cpu_support_stub.c
|
||||
LINUX_SRCS := $(COMMON_SRCS)
|
||||
BASE_ASSETS := $(filter-out %.dds,$(wildcard assets/*))
|
||||
ifneq ($(filter linux,$(MAKECMDGOALS)),)
|
||||
@@ -35,22 +35,60 @@ ASSETS := $(filter-out assets/icon0.png,$(BASE_ASSETS))
|
||||
endif
|
||||
GEN_SRCS := $(patsubst assets/%,gen/%, $(ASSETS:=.c))
|
||||
|
||||
# Vendored third-party: zlib + minizip-ng (ZIP), dmc_unrar (RAR). Compiled with
|
||||
# relaxed warnings (-w) — these are not our code and we do not want to chase
|
||||
# upstream style updates on every SDK upgrade.
|
||||
THIRD_PARTY_SRCS := $(wildcard third_party/zlib/src/*.c) $(wildcard third_party/minizip-ng/src/*.c) third_party/unrar/dmc_unrar.c
|
||||
THIRD_PARTY_CFLAGS := -O2 -w -Ithird_party/zlib/include -Ithird_party/minizip-ng/include -Ithird_party/unrar \
|
||||
-DHAVE_ZLIB -DZLIB_COMPAT -DHAVE_UNISTD_H=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
|
||||
-DDMC_UNRAR_DISABLE_BE32TOH_BE64TOH=1
|
||||
PS5_TP_OBJS := $(patsubst %.c,ps5-obj/%.o,$(THIRD_PARTY_SRCS))
|
||||
LINUX_TP_OBJS := $(patsubst %.c,linux-obj/%.o,$(THIRD_PARTY_SRCS))
|
||||
# Vendored third-party: zlib + minizip-ng (ZIP, C) and unrar 7.20.1 (RAR,
|
||||
# C++). unrar sources are compiled as a static library in RARDLL mode (no
|
||||
# main()); the project talks to it through the extern "C" DLL API in
|
||||
# third_party/unrar7/unrar_c_api.h. Compiled with relaxed warnings (-w) —
|
||||
# these are not our code and we do not want to chase upstream style updates.
|
||||
#
|
||||
# C++ compilers: PS5 uses prospero-clang++ (FreeBSD-style sysroot; the
|
||||
# toolchain defaults to -stdlib=libc++, driver links libc++ automatically);
|
||||
# host builds use the plain host C++ compiler (libstdc++).
|
||||
CXX ?= $(dir $(CC))prospero-clang++
|
||||
HOST_CXX ?= c++
|
||||
|
||||
CFLAGS := -Oz -fno-asynchronous-unwind-tables -fno-unwind-tables -Wall -Werror -ffunction-sections -fdata-sections -Isrc -Ithird_party/minizip-ng/include -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
|
||||
# Source set mirrors UnRARDll.vcxproj's ClCompile list (49 files) MINUS the
|
||||
# Windows-only isnt.cpp / motw.cpp (they need windows.h; the official unrar
|
||||
# UNIX makefile omits them, and PS5/linux both use the _UNIX branch where
|
||||
# their symbols are #ifdef'd out).
|
||||
UNRAR7_SRCS := \
|
||||
third_party/unrar7/archive.cpp third_party/unrar7/arcread.cpp third_party/unrar7/blake2s.cpp \
|
||||
third_party/unrar7/cmddata.cpp third_party/unrar7/consio.cpp third_party/unrar7/crc.cpp \
|
||||
third_party/unrar7/crypt.cpp third_party/unrar7/dll.cpp third_party/unrar7/encname.cpp \
|
||||
third_party/unrar7/errhnd.cpp third_party/unrar7/extinfo.cpp third_party/unrar7/extract.cpp \
|
||||
third_party/unrar7/filcreat.cpp third_party/unrar7/file.cpp third_party/unrar7/filefn.cpp \
|
||||
third_party/unrar7/filestr.cpp third_party/unrar7/find.cpp third_party/unrar7/getbits.cpp \
|
||||
third_party/unrar7/global.cpp third_party/unrar7/hash.cpp third_party/unrar7/headers.cpp \
|
||||
third_party/unrar7/largepage.cpp third_party/unrar7/match.cpp \
|
||||
third_party/unrar7/options.cpp third_party/unrar7/pathfn.cpp \
|
||||
third_party/unrar7/qopen.cpp third_party/unrar7/rar.cpp third_party/unrar7/rarpch.cpp \
|
||||
third_party/unrar7/rarvm.cpp third_party/unrar7/rawread.cpp third_party/unrar7/rdwrfn.cpp \
|
||||
third_party/unrar7/rijndael.cpp third_party/unrar7/rs.cpp third_party/unrar7/rs16.cpp \
|
||||
third_party/unrar7/scantree.cpp third_party/unrar7/secpassword.cpp third_party/unrar7/sha1.cpp \
|
||||
third_party/unrar7/sha256.cpp third_party/unrar7/smallfn.cpp third_party/unrar7/strfn.cpp \
|
||||
third_party/unrar7/strlist.cpp third_party/unrar7/system.cpp third_party/unrar7/threadpool.cpp \
|
||||
third_party/unrar7/timefn.cpp third_party/unrar7/ui.cpp third_party/unrar7/unicode.cpp \
|
||||
third_party/unrar7/unpack.cpp third_party/unrar7/volume.cpp
|
||||
|
||||
THIRD_PARTY_C_SRCS := $(wildcard third_party/zlib/src/*.c) $(wildcard third_party/minizip-ng/src/*.c)
|
||||
THIRD_PARTY_C_FLAGS := -O2 -w -Ithird_party/zlib/include -Ithird_party/minizip-ng/include \
|
||||
-DHAVE_ZLIB -DZLIB_COMPAT -DHAVE_UNISTD_H=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
|
||||
UNRAR7_CXX_FLAGS := -O2 -w -std=c++17 -DRARDLL -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
# prospero-clang++ defaults to -stdlib=libc++; state it explicitly for clarity.
|
||||
UNRAR7_CXX_FLAGS_PS5 := $(UNRAR7_CXX_FLAGS) -stdlib=libc++
|
||||
UNRAR7_CXX_FLAGS_HOST:= $(UNRAR7_CXX_FLAGS)
|
||||
|
||||
PS5_TP_OBJS := $(patsubst %.c,ps5-obj/%.o,$(THIRD_PARTY_C_SRCS)) \
|
||||
$(patsubst %.cpp,ps5-obj/%.o,$(UNRAR7_SRCS))
|
||||
LINUX_TP_OBJS := $(patsubst %.c,linux-obj/%.o,$(THIRD_PARTY_C_SRCS)) \
|
||||
$(patsubst %.cpp,linux-obj/%.o,$(UNRAR7_SRCS))
|
||||
|
||||
CFLAGS := -Oz -fno-asynchronous-unwind-tables -fno-unwind-tables -Wall -Werror -ffunction-sections -fdata-sections -Isrc -Ithird_party/minizip-ng/include -Ithird_party/unrar7 -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
|
||||
CFLAGS += `$(PKG_CONFIG) libmicrohttpd --cflags`
|
||||
LDFLAGS := -Wl,--gc-sections
|
||||
LDADD := `$(PKG_CONFIG) libmicrohttpd --libs`
|
||||
LDADD += -lSceIpmi -lSceAppInstUtil -lSceUserService
|
||||
LINUX_CFLAGS := -O2 -flto -Wall -Werror -Isrc -Ithird_party/minizip-ng/include -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
|
||||
LINUX_CFLAGS := -O2 -flto -Wall -Werror -Isrc -Ithird_party/minizip-ng/include -Ithird_party/unrar7 -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
|
||||
LINUX_CFLAGS += `$(HOST_PKG_CONFIG) libmicrohttpd --cflags`
|
||||
LINUX_LDADD := `$(HOST_PKG_CONFIG) libmicrohttpd --libs` -pthread
|
||||
|
||||
@@ -78,16 +116,28 @@ gen/%.c: assets/% gen-asset-module.py | gen
|
||||
|
||||
ps5-obj/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(THIRD_PARTY_CFLAGS) -c -o $@ $<
|
||||
$(CC) $(THIRD_PARTY_C_FLAGS) -c -o $@ $<
|
||||
|
||||
linux-obj/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CC) $(THIRD_PARTY_CFLAGS) -c -o $@ $<
|
||||
$(HOST_CC) $(THIRD_PARTY_C_FLAGS) -c -o $@ $<
|
||||
|
||||
ps5-obj/%.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
$(CXX) $(UNRAR7_CXX_FLAGS_PS5) -c -o $@ $<
|
||||
|
||||
linux-obj/%.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
$(HOST_CXX) $(UNRAR7_CXX_FLAGS_HOST) -c -o $@ $<
|
||||
|
||||
# Link with the C++ driver so libc++ (PS5) / libstdc++ (host) is pulled in
|
||||
# automatically for the unrar objects. The project's own C sources are passed
|
||||
# through -x c (clang++ would otherwise compile .c files as C++ and trip
|
||||
# -Wdeprecated); -x none restores extension-based handling for the .o files.
|
||||
$(BIN): $(PS5_SRCS) $(GEN_SRCS) $(PS5_TP_OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(filter %.c,$^) $(PS5_TP_OBJS) $(LDADD)
|
||||
$(CXX) $(CFLAGS) $(LDFLAGS) -o $@ -x c $(filter %.c,$^) -x none $(PS5_TP_OBJS) $(LDADD)
|
||||
$(STRIP) $@
|
||||
|
||||
$(LINUX_BIN): $(LINUX_SRCS) $(GEN_SRCS) $(LINUX_TP_OBJS)
|
||||
$(HOST_CC) $(LINUX_CFLAGS) -o $@ $(filter %.c,$^) $(LINUX_TP_OBJS) $(LINUX_LDADD)
|
||||
$(HOST_CXX) $(LINUX_CFLAGS) -o $@ -x c $(filter %.c,$^) -x none $(LINUX_TP_OBJS) $(LINUX_LDADD)
|
||||
$(HOST_STRIP) $@
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Homebrew HTTP file manager for jailbroken PS5 consoles. Browse, edit, upload, download and extract ZIPs through any browser on the same network — single self-contained ELF payload, no external services, no telemetry.
|
||||
|
||||
**Version:** v1.8 · **Title ID:** `FMGR88888` · **License:** GPLv3+ · **Target:** `x86_64-sie-ps5`
|
||||
**Version:** v1.9 · **Title ID:** `FMGR88888` · **License:** GPLv3+ · **Target:** `x86_64-sie-ps5`
|
||||
|
||||
---
|
||||
|
||||
@@ -12,6 +12,22 @@ A payload ELF that runs an HTTP file manager inside a jailbroken PS5. Open `http
|
||||
|
||||
The same source tree builds a Linux binary for development and a PS5 payload ELF for deployment — see `make linux` below.
|
||||
|
||||
## What's new in v1.9
|
||||
|
||||
- **RAR engine replaced with the official rarlab UnRAR 7.20.1**
|
||||
(`third_party/unrar7/`, replacing dmc_unrar). This is what actually
|
||||
makes RAR extraction work on real files: dmc_unrar could not decode
|
||||
archives written by **WinRAR 6.x/7.x** (RAR5 "v6" compression) and had
|
||||
no multi-volume support — both now work.
|
||||
- **RAR5 "v6" archives extract** (the v1.8-era "corrupt archive" report
|
||||
on WinRAR 6/7 files is gone).
|
||||
- **Multi-volume RAR** (`.part01.rar` chains): unrar stitches the parts by
|
||||
name when the full set sits next to the volume you open.
|
||||
- Engine can decrypt encrypted RAR (`RARSetPassword`) — password UI /
|
||||
API plumbing still pending, encrypted archives are rejected for now.
|
||||
- Host tests now run real archives (v6 / encrypted / 3-volume fixtures
|
||||
committed under `tests/fixtures-real/`): **70 ZIP + 24 RAR = 94 checks**.
|
||||
|
||||
## What's new in v1.8
|
||||
|
||||
- **Single-volume RAR extraction** via the vendored FLOSS library
|
||||
@@ -40,10 +56,52 @@ The same source tree builds a Linux binary for development and a PS5 payload ELF
|
||||
limitations that come from using dmc_unrar (no multi-volume, no
|
||||
encryption in v1.8 — both lift in v1.9 when the library is replaced).
|
||||
|
||||
## What's new in v1.8.1
|
||||
|
||||
- **Default ZIP limits relaxed** (companion to v1.7's large profile).
|
||||
v1.7 shipped with a 64 GiB default per-entry cap, which was too
|
||||
aggressive for typical PS5 system-backup ZIPs (200-300 GiB). v1.8.1
|
||||
raises the default profile to **1 TiB total / 256 GiB per entry /
|
||||
500 : 1 ratio**, with the `large=1` opt-in kept at 2 TiB / 1 TiB /
|
||||
1000 : 1. The frontend threshold rises from 60 GiB to 240 GiB so
|
||||
common system-backup archives no longer trigger the prompt.
|
||||
- RAR extraction inherits the new defaults (rar_extract.c threads
|
||||
`c->limits` from the engine — no engine change required).
|
||||
- Rationale: the real zip-bomb defence is `check_space()` (statvfs-based
|
||||
real disk-space check before staging) + `max_ratio` (declared
|
||||
compression ratio cap). The size caps are a UX guard, not a security
|
||||
boundary.
|
||||
|
||||
## What's new in v1.8.2
|
||||
|
||||
- **Default ZIP limits relaxed again** for the 3A-game single-file case.
|
||||
A single ~300 GiB uncompressed file inside an archive was still
|
||||
silently rejected by v1.8.1 (the default scan returns
|
||||
`ZIPX_ERR_LIMIT_FILE_SIZE` before the request ever reaches the
|
||||
frontend confirmation prompt). v1.8.2 raises the default profile to
|
||||
**2 TiB total / 512 GiB per entry / 500 : 1 ratio**, with the `large=1`
|
||||
opt-in bumped to 4 TiB / 1 TiB / 1000 : 1. Frontend threshold rises
|
||||
from 240 GiB to 480 GiB.
|
||||
- **Two PS5-only build fixes** discovered when cross-compiling for the
|
||||
PS5 target. The host-side test suite (`tests/run-tests.sh`) had
|
||||
silently accepted both because it links the same sources but uses
|
||||
gcc rather than clang 18 and a different include path:
|
||||
- `Makefile` CFLAGS: add `-Ithird_party/unrar` so `src/rar_extract.c`
|
||||
can find the project-authored `dmc_unrar_api.h` facade header.
|
||||
- `src/extract.c`: move `extract_progress()` definition above
|
||||
`extract_dispatch()` so the implicit function declaration is not
|
||||
flagged by `-Werror=implicit-function-declaration` (clang 18 in the
|
||||
PS5 SDK is stricter than the host gcc used by tests).
|
||||
- **Release artifact** for v1.8.2: `web-file-mgr.elf` — 509 704 bytes,
|
||||
sha256 `1b2c3d68b35e32737105f17d14a80a3c159ceca0cabd274ee168cbcd81906f65`,
|
||||
ELF class 64, little-endian, e_machine `0x003e` (x86_64-sie-ps5).
|
||||
- Tests: **84 host-side checks** (70 ZIP + 14 RAR), 0 failures. PS5
|
||||
cross-compile succeeds end-to-end.
|
||||
|
||||
## What's new in v1.7
|
||||
|
||||
- **ZIP large-file profile** (opt-in via the new `large=1` argument on `/api/extract`): relaxed caps of **2 TiB** archive total, **1 TiB** per entry, **1000 : 1** compression ratio. The frontend prompts for confirmation whenever the archive on disk is larger than **60 GiB**; the server only activates the profile when the user explicitly agrees.
|
||||
- **Stricter default ZIP profile** stays safe: **512 GiB** total / **64 GiB** per entry / **200 : 1** ratio. A 4 MiB compressed payload that expands to 800 GiB still gets rejected before any output file is opened.
|
||||
- **Stricter default ZIP profile** stays safe: **1 TiB** total / **256 GiB** per entry / **500 : 1** ratio. A 4 MiB compressed payload that expands to 800 GiB still gets rejected before any output file is opened.
|
||||
- **69 host-side C tests** (`tests/run-tests.sh`) now cover path traversal, ZIP64, encryption rejection, ratios, conflict policies and the new large-file profile (`tests/test_zip_extract.c`).
|
||||
- Earlier refinements — see `git log` since v1.6.
|
||||
|
||||
@@ -68,7 +126,7 @@ The same source tree builds a Linux binary for development and a PS5 payload ELF
|
||||
- **Upload** — single files or folder trees from any device on the LAN (hidden in the PS5 browser). Atomic temp + rename.
|
||||
- **Download** — single file as raw bytes, or folders/multi-select as a streaming `.tar`. Hidden in the PS5 browser.
|
||||
- **Tasks** — full-screen overlay with delayed show, live progress, throughput, ETA, cancel, and recovery if the browser is closed and reopened mid-task.
|
||||
- **Archive extraction** — ZIP (encrypted rejected) and RAR (single-volume unencrypted); see the [ZIP extraction](#zip-extraction) and [RAR extraction](#rar-extraction) sections below for scope.
|
||||
- **Archive extraction** — ZIP (encrypted rejected) and RAR (v1.9: RAR4 + RAR5 incl. WinRAR 6/7 "v6", multi-volume; encrypted still rejected pending password UI); see the [ZIP extraction](#zip-extraction) and [RAR extraction](#rar-extraction) sections below for scope.
|
||||
- **PKG** — install and preview `.pkg` files.
|
||||
- **Images** — preview `.png .jpg .jpeg .gif .bmp .webp`.
|
||||
- **Localization** — English + Simplified Chinese, auto-selected from `navigator.languages`.
|
||||
@@ -116,7 +174,7 @@ make
|
||||
Output:
|
||||
|
||||
```text
|
||||
web-file-mgr.elf (~418 KiB, x86_64-sie-ps5)
|
||||
web-file-mgr.elf (~several hundred KiB, larger in v1.9 with unrar; x86_64-sie-ps5)
|
||||
```
|
||||
|
||||
For pure UI/JS work without the PS5 toolchain:
|
||||
@@ -156,13 +214,13 @@ Plain ZIPs only — stored / deflated / ZIP64, **never encrypted**. The engine i
|
||||
| Limit | Default profile | Large profile (`ZIPX_LIMITS_LARGE`) |
|
||||
|---|---|---|
|
||||
| `max_entries` | 200 000 | 500 000 |
|
||||
| `max_total_bytes` (uncompressed) | 512 GiB | 2 TiB |
|
||||
| `max_file_bytes` (per entry) | 64 GiB | 1 TiB |
|
||||
| `max_ratio` (uncompressed / compressed) | 200 : 1 | 1000 : 1 |
|
||||
| `max_total_bytes` (uncompressed) | 2 TiB | 4 TiB |
|
||||
| `max_file_bytes` (per entry) | 512 GiB | 1 TiB |
|
||||
| `max_ratio` (uncompressed / compressed) | 500 : 1 | 1000 : 1 |
|
||||
| `max_depth` (folder nesting) | 32 | 32 |
|
||||
| `max_name_len` / `max_path_len` | 255 / 1024 | 255 / 1024 |
|
||||
|
||||
The **default profile** is shipped safe: a 4 MiB compressed blob that decodes to 800 GiB is rejected before any output file is opened. The **large profile** is engaged **only** when the request includes `large=1` — the archive dialog prompts the user automatically whenever the archive on disk is larger than `LARGE_FILE_THRESHOLD_BYTES` (60 GiB by default; configurable in `assets/main.js`). Confirming the prompt is the user's explicit opt-in; the server still records nothing extra on its own.
|
||||
The **default profile** is shipped safe: a 4 MiB compressed blob that decodes to 800 GiB is rejected before any output file is opened. The **large profile** is engaged **only** when the request includes `large=1` — the archive dialog prompts the user automatically whenever the archive on disk is larger than `LARGE_FILE_THRESHOLD_BYTES` (480 GiB by default; configurable in `assets/main.js`). Confirming the prompt is the user's explicit opt-in; the server still records nothing extra on its own.
|
||||
|
||||
### Security checks
|
||||
|
||||
@@ -184,41 +242,42 @@ Passed as `conflict=` on `/api/extract`:
|
||||
|
||||
### Tuning the threshold
|
||||
|
||||
The 60 GiB frontend threshold lives in `assets/main.js`:
|
||||
The 480 GiB frontend threshold lives in `assets/main.js`:
|
||||
|
||||
```js
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 480 * 1024 * 1024 * 1024;
|
||||
```
|
||||
|
||||
Set it to `Infinity` to silence the prompt, lower it to be more conservative, or remove the call entirely — the server still respects `large=1` regardless of the threshold.
|
||||
|
||||
## RAR extraction
|
||||
|
||||
A single-volume, **unencrypted** RAR archive engine (`src/rar_extract.{c,h}`,
|
||||
backed by the vendored FLOSS library
|
||||
[`dmc_unrar`](https://github.com/DrMcCoy/dmc_unrar) at
|
||||
`third_party/unrar/dmc_unrar.c`). Files with the extension `.rar` get the
|
||||
same **Extract** button as `.zip` files; the engine is dispatched by
|
||||
`src/extract.c` based on extension.
|
||||
A RAR extraction engine (`src/rar_extract.{c,h}`) backed by the **official
|
||||
rarlab UnRAR source** (`third_party/unrar7/`, version 7.20.1, compiled as a
|
||||
static library and driven through its C-compatible DLL API). Files with the
|
||||
extension `.rar` get the same **Extract** button as `.zip` files; the engine
|
||||
is dispatched by `src/extract.c` based on extension.
|
||||
|
||||
> v1.9 replaced the v1.8 engine (dmc_unrar 1.7.0). dmc_unrar could not
|
||||
> decode archives written by WinRAR 6.x/7.x (RAR5 "v6" compression) and had
|
||||
> no multi-volume support; unrar handles both natively.
|
||||
|
||||
### Scope
|
||||
|
||||
| Format | Support | Notes |
|
||||
|---|---|---|
|
||||
| RAR 1.5 / 2.0 / 2.6 / 2.9 / 3.0 / 3.6 / 4.0 | ✅ | Single-volume, unencrypted |
|
||||
| RAR 5.0 | ✅ | Single-volume, unencrypted |
|
||||
| Solid blocks, dictionary 4 MiB (RAR4) / 32 MiB (RAR5) | ✅ | |
|
||||
| RAR 1.5 → 4.x (incl. 2.9 / 3.6 / 4.0) | ✅ | |
|
||||
| RAR 5.0 and **5.0 "v6"** (WinRAR 6.x / 7.x) | ✅ | The v1.9 trigger |
|
||||
| Solid blocks, dictionary up to 1 GiB | ✅ | |
|
||||
| PPMd decompression (RAR 3.0+) | ✅ | |
|
||||
| **Multi-volume** (`.part01.rar` + `.part02.rar` + …) | ❌ | Rejected with `ZIPX_ERR_UNSUPPORTED`. The frontend greys out non-`01` sub-volumes with a tooltip. Join / unrar on a PC first. |
|
||||
| **Encrypted RAR** (any encrypted header or file) | ❌ | Rejected with `ZIPX_ERR_UNSUPPORTED`. There is no `password=` field in `/api/extract`. |
|
||||
| **Multi-volume** (`.part01.rar` + `.part02.rar` + …) | ✅ | unrar stitches parts by name when the whole set sits next to the volume you open. Select the first volume (`name.part1.rar` / `name.part01.rar`); non-first volumes are still greyed out in the UI with a hint. |
|
||||
| **Encrypted RAR** | ⏳ | The engine can decrypt (`RARSetPassword`), but the password field / prompt is not wired into `/api/extract` yet. Encrypted archives are rejected up front with `ZIPX_ERR_UNSUPPORTED`. |
|
||||
| Symbolic links / FIFOs / sockets / devices | ❌ | Rejected with `ZIPX_ERR_SPECIAL` (mirrors ZIP behaviour) |
|
||||
| RAR 1.4 (very old) | ❌ | Not supported by dmc_unrar 1.7.0; rejected upstream |
|
||||
| RAR 1.3 (pre-1.4) | ❌ | Rejected upstream by unrar |
|
||||
|
||||
The "extract on a PC first" recovery is the same escape hatch the engine
|
||||
uses for ZIP encryption errors: when an unsupported archive is rejected,
|
||||
the user gets an `extract_unsupported` failure with the file name as the
|
||||
detail argument. The frontend already shows this with the typical
|
||||
bilingual retry guidance.
|
||||
When an archive is rejected, the user gets an `extract_unsupported`
|
||||
failure with the file name as the detail argument. The frontend already
|
||||
shows this with the typical bilingual retry guidance.
|
||||
|
||||
### Limits
|
||||
|
||||
@@ -228,14 +287,14 @@ profile table on top. Defaults and the `large=1` opt-in are identical:
|
||||
| Limit | Default profile | Large profile (`large=1`) |
|
||||
|---|---|---|
|
||||
| `max_entries` | 200 000 | 500 000 |
|
||||
| `max_total_bytes` (uncompressed) | 512 GiB | 2 TiB |
|
||||
| `max_file_bytes` (per entry) | 64 GiB | 1 TiB |
|
||||
| `max_ratio` (uncompressed / compressed) | 200 : 1 | 1000 : 1 |
|
||||
| `max_total_bytes` (uncompressed) | 2 TiB | 4 TiB |
|
||||
| `max_file_bytes` (per entry) | 512 GiB | 1 TiB |
|
||||
| `max_ratio` (uncompressed / compressed) | 500 : 1 | 1000 : 1 |
|
||||
| `max_depth` (folder nesting) | 32 | 32 |
|
||||
| `max_name_len` / `max_path_len` | 255 / 1024 | 255 / 1024 |
|
||||
|
||||
Large-profile RAR extraction uses the same `LARGE_FILE_THRESHOLD_BYTES`
|
||||
(60 GiB) prompt as ZIP — the frontend treats `.rar` and `.zip` the same
|
||||
(480 GiB) prompt as ZIP — the frontend treats `.rar` and `.zip` the same
|
||||
way for the prompt, and the server only ever activates the large caps
|
||||
when the request carries `large=1` (opt-in).
|
||||
|
||||
@@ -255,37 +314,32 @@ The RAR engine applies the same checks as the ZIP engine — re-uses
|
||||
|
||||
### Vendoring and licence
|
||||
|
||||
`third_party/unrar/dmc_unrar.c` is vendored **verbatim** from
|
||||
[`DrMcCoy/dmc_unrar`](https://github.com/DrMcCoy/dmc_unrar), upstream
|
||||
commit pinned at the same date as the v1.8 release. The file is
|
||||
**GPL-2.0-or-later** (see `third_party/unrar/COPYING`), which means the
|
||||
resulting `web-file-mgr.elf` is also effectively GPL-2.0-or-later. The
|
||||
already-GPLv3+ project is forward-compatible with that, and the
|
||||
compliant distribution form (binary + corresponding sources + GPL
|
||||
notice alongside the LGPL notice for libmicrohttpd) is the same
|
||||
process you already follow for every prior release. The minimal
|
||||
project-authored facade `third_party/unrar/dmc_unrar_api.h` carries the
|
||||
project's own licence and is *not* bound to GPL.
|
||||
`third_party/unrar7/` is a verbatim copy of the official **rarlab UnRAR
|
||||
source** (7.20.1), mirrored by
|
||||
[`opello/unrar`](https://github.com/opello/unrar) at commit `97e1780`. It is
|
||||
distributed under the **UnRAR freeware licence** (see
|
||||
`third_party/unrar7/license.txt`): it may be used in any software to handle
|
||||
RAR archives, but may not be used to develop a RAR-compatible *archiver* or
|
||||
re-create the RAR compression algorithm. The project-authored facade
|
||||
`third_party/unrar7/unrar_c_api.h` carries the project's own licence.
|
||||
|
||||
### v1.9 plan
|
||||
> The v1.8 engine `third_party/unrar/dmc_unrar.c` (DrMcCoy/dmc_unrar 1.7.0,
|
||||
> GPL-2.0-or-later) was removed in v1.9; its notice lives in git history.
|
||||
|
||||
When (if) multi-volume RAR and encrypted RAR become worth the
|
||||
engineering cost, the recommended path is to replace
|
||||
`third_party/unrar/dmc_unrar.c` with a vendored mirror of
|
||||
[`opello/unrar`](https://github.com/opello/unrar) (a faithful copy of
|
||||
rarlab UnRAR 7.x, C++17, supports volumes + encryption). The
|
||||
`rar_extract()` signature, the dispatch layer, and the host tests do
|
||||
**not** need to change — only the engine behind `rar_extract()` and the
|
||||
`password=` field on `/api/extract`. See
|
||||
[`third_party/unrar/VENDORED.md`](./third_party/unrar/VENDORED.md) for
|
||||
the step-by-step upgrade recipe.
|
||||
### Encrypted RAR (planned)
|
||||
|
||||
The engine can decrypt archives (via `RARSetPassword`), but the password
|
||||
channel — a `password=` field on `/api/extract` plus a frontend prompt —
|
||||
is not wired yet. Encrypted archives currently fail with
|
||||
`extract_unsupported`. The engine swap (v1.9) removed the hard engine
|
||||
limits; the remaining work is purely API/UI plumbing.
|
||||
|
||||
## Verification
|
||||
|
||||
After `make`, sanity-check the produced ELF:
|
||||
|
||||
```sh
|
||||
ls -la web-file-mgr.elf # size ~430 KiB on v1.8 (~418 KiB on v1.7)
|
||||
ls -la web-file-mgr.elf # size grew in v1.9 (unrar static library); ~509 KiB was v1.8.3
|
||||
sha256sum web-file-mgr.elf # record the digest in your release notes
|
||||
file web-file-mgr.elf # expect "ELF 64-bit LSB pie executable, x86-64"
|
||||
od -An -tx1 -N20 web-file-mgr.elf | head -2 # magic 7f45 4c46 0201 + e_machine 003e
|
||||
@@ -301,8 +355,8 @@ A POSIX/host-side C test suite covers the ZIP engine and runs on any Linux / mac
|
||||
cd tests && bash run-tests.sh
|
||||
```
|
||||
|
||||
Output is a per-case `check`-style report — **83 checks** on the current `main`
|
||||
(69 ZIP + 14 RAR). Coverage:
|
||||
Output is a per-case `check`-style report — **84 checks** on the current `main`
|
||||
(70 ZIP + 14 RAR). Coverage:
|
||||
|
||||
- ZIP entry parsing (stored + deflated + ZIP64)
|
||||
- Path traversal, absolute paths, backslash, Windows drive letters
|
||||
@@ -320,7 +374,7 @@ Output is a per-case `check`-style report — **83 checks** on the current `main
|
||||
|
||||
```
|
||||
.
|
||||
├── Makefile # PS5 + Linux builds (VERSION_TAG v1.8)
|
||||
├── Makefile # PS5 + Linux builds (VERSION_TAG v1.8.2)
|
||||
├── install-libmicrohttpd.sh # one-shot dependency installer
|
||||
├── gen-asset-module.py # embeds assets/* as gzip-compressed C arrays
|
||||
├── assets/ # HTML / CSS / JS / icons / param.json
|
||||
@@ -368,10 +422,11 @@ Output is a per-case `check`-style report — **83 checks** on the current `main
|
||||
- **This is a homebrew app and should not intentionally modify system processes or kernel memory.** If you hit a kernel panic, make sure you are using a recent jailbreak method and ELF loader, or revert to the stable method you normally use.
|
||||
- **P2JB users** — if this payload triggers a kernel panic, avoid using it on that setup. Stability matters more than convenience when each retry is expensive.
|
||||
- **The preparing stage can take a while** when a folder contains many files — it sums folder size and checks free space, which helps avoid starting a copy / move / upload / download that cannot finish safely.
|
||||
- **`err_extract_entry_too_large`** — default archive caps are 64 GiB per
|
||||
entry / 200:1 ratio. Confirm the large-file prompt (appears for
|
||||
archives > 60 GiB on disk), split the archive, or pass `large=1`
|
||||
directly to the API.
|
||||
- **`err_extract_entry_too_large`** — default archive caps are 512 GiB per
|
||||
entry / 500:1 ratio (covers a typical 3A-game archive with one ~300 GiB
|
||||
uncompressed file). If you exceed the default, confirm the large-file
|
||||
prompt (appears for archives > 480 GiB on disk), split the archive, or
|
||||
pass `large=1` directly to the API.
|
||||
- **`err_extract_unsupported`** — the archive uses a feature the engine
|
||||
cannot handle: encrypted ZIP, encrypted RAR, multi-volume RAR
|
||||
(`.part02+.rar`), very-old RAR 1.4, RAR symlinks / FIFOs, or a file
|
||||
|
||||
+27
-23
@@ -2,8 +2,9 @@ Third-Party Notices
|
||||
===================
|
||||
|
||||
This project vendors a minimal set of third-party source files under
|
||||
`third_party/` to provide the ZIP extraction feature (the `/api/extract`
|
||||
endpoint). Their full license texts are included alongside the sources.
|
||||
`third_party/` to provide the ZIP and RAR extraction features (the
|
||||
`/api/extract` endpoint). Their full license texts are included
|
||||
alongside the sources.
|
||||
|
||||
1. minizip-ng
|
||||
-----------
|
||||
@@ -21,26 +22,29 @@ endpoint). Their full license texts are included alongside the sources.
|
||||
Files : third_party/zlib/** (vendored subset, compiled with relaxed
|
||||
warnings into the final binary)
|
||||
|
||||
3. dmc_unrar
|
||||
---------
|
||||
Version : 1.7.0
|
||||
Source : https://github.com/DrMcCoy/dmc_unrar
|
||||
License : GPL-2.0-or-later (see third_party/unrar/COPYING)
|
||||
Files : third_party/unrar/dmc_unrar.c (single-file library, compiled
|
||||
with relaxed warnings; the small facade header
|
||||
third_party/unrar/dmc_unrar_api.h is project-authored and
|
||||
carries the project's license).
|
||||
3. unrar (rarlab UnRAR source, v7.20.1)
|
||||
-------------------------------------
|
||||
Version : 7.20.1 (RAR 7.23-free source snapshot, 2025-10-28)
|
||||
Source : https://www.rarlab.com/rar_add.htm — mirrored by
|
||||
https://github.com/opello/unrar (commit 97e1780)
|
||||
License : UnRAR freeware license (see third_party/unrar7/license.txt)
|
||||
Files : third_party/unrar7/** (RARDLL source set compiled with
|
||||
relaxed warnings; unrar_c_api.h is project-authored and
|
||||
carries the project's license)
|
||||
|
||||
dmc_unrar is the engine behind `src/rar_extract.c` (the v1.8 RAR support).
|
||||
It is a dependency-free, single-file, C89/C99 FLOSS UnRAR library. Because
|
||||
it is licensed under the GPL-2.0-or-later, the resulting `web-file-mgr.elf`
|
||||
binary inherits the obligation to ship the GPL notice and source — which is
|
||||
exactly what this file plus `third_party/unrar/COPYING` provides. The
|
||||
interface in `dmc_unrar_api.h` is minimal and re-declares only the entry
|
||||
points the project uses.
|
||||
unrar is the engine behind `src/rar_extract.c` (the v1.9 RAR support: RAR4,
|
||||
RAR5 including WinRAR 6/7 "v6" compression, and multi-volume archives; the
|
||||
engine can also decrypt via RARSetPassword once a password channel is wired
|
||||
up). The UnRAR source may be used in any software to handle RAR archives,
|
||||
but may not be used to develop a RAR-compatible *archiver* or to re-create
|
||||
the RAR compression algorithm, which is proprietary.
|
||||
|
||||
Both libraries in section 1 and 2 are distributed under the zlib license,
|
||||
which permits redistribution in source and binary form provided the
|
||||
copyright notice and this list of conditions are retained. dmc_unrar is
|
||||
distributed under the GNU GPL v2-or-later. See the individual LICENSE /
|
||||
COPYING files in each `third_party/` subdirectory for the complete terms.
|
||||
(The v1.8 engine, DrMcCoy/dmc_unrar 1.7.0 under GPL-2.0-or-later, was
|
||||
replaced by the rarlab UnRAR source in v1.9; see git history under
|
||||
`third_party/unrar/` for its notice.)
|
||||
|
||||
Libraries in sections 1 and 2 are distributed under the zlib license, which
|
||||
permits redistribution in source and binary form provided the copyright
|
||||
notice and this list of conditions are retained. unrar is distributed under
|
||||
its own freeware terms. See the individual LICENSE / license.txt files in
|
||||
each `third_party/` subdirectory for the complete terms.
|
||||
+1
-1
@@ -68,7 +68,7 @@
|
||||
</section>
|
||||
<input id="uploadFiles" type="file" multiple hidden>
|
||||
<input id="uploadFolder" type="file" multiple webkitdirectory hidden>
|
||||
<input id="uploadZip" type="file" accept=".zip,application/zip,application/x-zip-compressed" hidden>
|
||||
<input id="uploadZip" type="file" accept=".zip,.rar,application/zip,application/x-zip-compressed,application/vnd.rar,application/x-rar-compressed" hidden>
|
||||
|
||||
<section id="content" class="content">
|
||||
<table>
|
||||
|
||||
+2
-2
@@ -101,11 +101,11 @@ window.WFM_LANG = {
|
||||
extracting: "Extracting",
|
||||
extractConfirm: "Extract {name} to {path}?",
|
||||
extractOverwriteAsk: "If a file or folder with the same name already exists in the target:\n\nOK = overwrite same-name files (folders still merge)\nCancel = fail if the target already exists",
|
||||
extractUploadConfirm: "Upload and extract {name}?\n\nTarget folder: {path}\nThe uploaded ZIP will be deleted after success.",
|
||||
extractUploadConfirm: "Upload and extract {name}?\n\nTarget folder: {path}\nThe uploaded archive will be deleted after success.",
|
||||
extractStarted: "Extraction started: {name}",
|
||||
extractDone: "Extraction complete: {name}",
|
||||
extractProgress: "{done} / {total} files",
|
||||
extractLargeAsk: "The archive looks large ({size}). Enable the large-file profile?\n\nOK = yes (single file up to 1 TiB, archive total up to 2 TiB)\nCancel = default limits (single file 64 GiB, archive total 512 GiB); this archive may be rejected",
|
||||
extractLargeAsk: "The archive looks large ({size}). Enable the large-file profile?\n\nOK = yes (single file up to 1 TiB, archive total up to 4 TiB)\nCancel = default limits (single file 512 GiB, archive total 2 TiB); this archive may be rejected",
|
||||
extractLargeActive: "Large-file profile is enabled for this task",
|
||||
extractSelectMainVolume: "Please select the main volume (.rar or .part01.rar)",
|
||||
extractArchivePending: "Preparing to extract {name}",
|
||||
|
||||
+2
-2
@@ -101,11 +101,11 @@ window.WFM_LANG = {
|
||||
extracting: "解压",
|
||||
extractConfirm: "解压 {name} 到 {path}?",
|
||||
extractOverwriteAsk: "若目标已存在同名文件或目录:\n\n确定 = 覆盖同名文件(目录仍会合并)\n取消 = 若目标已存在则失败",
|
||||
extractUploadConfirm: "上传并解压 {name}?\n\n目标目录:{path}\n成功后将删除上传的 ZIP。",
|
||||
extractUploadConfirm: "上传并解压 {name}?\n\n目标目录:{path}\n成功后将删除上传的压缩包。",
|
||||
extractStarted: "已开始解压 {name}",
|
||||
extractDone: "解压完成:{name}",
|
||||
extractProgress: "{done} / {total} 个文件",
|
||||
extractLargeAsk: "ZIP 体积较大({size}),是否启用「大文件模式」?\n\n确定 = 启用(单文件最大 1 TiB / 总解压最大 2 TiB)\n取消 = 默认限制(单文件 64 GiB / 总解压 512 GiB),可能拒绝此压缩包",
|
||||
extractLargeAsk: "压缩包体积较大({size}),是否启用「大文件模式」?\n\n确定 = 启用(单文件最大 1 TiB / 总解压最大 4 TiB)\n取消 = 默认限制(单文件 512 GiB / 总解压 2 TiB),可能拒绝此压缩包",
|
||||
extractLargeActive: "此任务已启用大文件模式",
|
||||
extractSelectMainVolume: "请改选主卷(如 .rar 或 .part01.rar)",
|
||||
extractArchivePending: "正在准备解压 {name}",
|
||||
|
||||
+9
-3
@@ -35,7 +35,7 @@ let uploadXhr = null;
|
||||
let uploadTerminalAbort = false;
|
||||
let L = {};
|
||||
|
||||
const APP_VERSION = "v1.7";
|
||||
const APP_VERSION = "v1.9";
|
||||
const LAST_PATH_KEY = "ps5-web-file-mgr:last-path";
|
||||
const SORT_KEY = "ps5-web-file-mgr:list-sort";
|
||||
const LOADING_DISPLAY_DELAY = 250;
|
||||
@@ -835,7 +835,13 @@ async function startExtractTask(path, dstDir, conflict, removeSource, name, larg
|
||||
}
|
||||
}
|
||||
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
|
||||
// Threshold above which the web UI prompts the user before extracting.
|
||||
// Tuned so a typical 3A-game archive (~300 GiB single file) and a PS5 system
|
||||
// backup (~300 GiB total) extract under the default profile without prompting.
|
||||
// Files between this threshold and the default max_file_bytes cap (512 GiB)
|
||||
// still extract silently; larger files require explicit user opt-in via the
|
||||
// large=1 API flag.
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 480 * 1024 * 1024 * 1024;
|
||||
|
||||
function shouldPromptLargeMode(itemSize) {
|
||||
return Number(itemSize || 0) > LARGE_FILE_THRESHOLD_BYTES;
|
||||
@@ -2331,7 +2337,7 @@ function actionUploadAndExtract() {
|
||||
|
||||
async function uploadAndExtractFile(file) {
|
||||
if (busy || loadingPath) return;
|
||||
if (!/\.zip$/i.test(file.name || "")) {
|
||||
if (!/\.(zip|rar)$/i.test(file.name || "")) {
|
||||
alert(t("err_extract_unsupported", { arg: file.name }));
|
||||
return;
|
||||
}
|
||||
|
||||
+6
-6
@@ -87,7 +87,7 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ Frontend: assets/main.js │
|
||||
│ - LARGE_FILE_THRESHOLD_BYTES = 60 GiB (硬编码) │
|
||||
│ - LARGE_FILE_THRESHOLD_BYTES = 240 GiB (硬编码) │
|
||||
│ - shouldPromptLargeMode(itemSize) → 弹 confirm │
|
||||
│ - startExtractTask(path, dst, conflict, remove, name, large) │
|
||||
└────────────────────┬───────────────────────────────────────────┘
|
||||
@@ -103,8 +103,8 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
|
||||
│
|
||||
┌────────────────────▼───────────────────────────────────────────┐
|
||||
│ Engine: src/zip_extract.{h,c} │
|
||||
│ - k_default_limits: 200K / 512GiB / 64GiB / 200:1 │
|
||||
│ - k_large_limits: 500K / 2TiB / 1TiB / 1000:1 │
|
||||
│ - k_default_limits: 200K / 1TiB / 256GiB / 500:1 │
|
||||
│ - k_large_limits: 500K / 2TiB / 1TiB / 1000:1 │
|
||||
│ - ZIPX_LIMITS_DEFAULT=0 / ZIPX_LIMITS_LARGE=1 │
|
||||
│ - zipx_limits_profile(int) → const zipx_limits_t* │
|
||||
│ - zipx_extract() 签名不变,向后兼容 │
|
||||
@@ -121,7 +121,7 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
|
||||
- `assets/lang-en.js` 和 `lang-zh.js` 新增 `extractLargeAsk` / `extractLargeActive`
|
||||
|
||||
**前端 UX**:
|
||||
- ZIP 大于 60 GiB 时弹窗「启用大文件模式?」
|
||||
- ZIP 大于 480 GiB 时弹窗「启用大文件模式?」
|
||||
- 用户点 OK → 传 `large=1` → 引擎走 large profile
|
||||
- 用户点取消 → 走 default profile(多半会被拒绝)
|
||||
|
||||
@@ -1130,8 +1130,8 @@ multi-volume (`name.part01.rar`, `name.part02.rar`, …). Encrypted
|
||||
archives prompt for a password client-side; the password is held
|
||||
only in memory and never saved.
|
||||
|
||||
Limits mirror the ZIP profiles (200K entries / 512 GiB / 64 GiB /
|
||||
ratio 200 by default, with the same `large=1` opt-in to 500K /
|
||||
Limits mirror the ZIP profiles (200K entries / 1 TiB / 256 GiB /
|
||||
ratio 500 by default, with the same `large=1` opt-in to 500K /
|
||||
2 TiB / 1 TiB / 1000).
|
||||
```
|
||||
|
||||
|
||||
@@ -416,10 +416,10 @@ self-evident from the failure.
|
||||
|
||||
### 7.4 The `large=1` prompt for RAR
|
||||
|
||||
The threshold is shared. A `.rar` larger than `60 GiB` triggers the
|
||||
The threshold is shared. A `.rar` larger than `240 GiB` triggers the
|
||||
same `promptLargeMode()` confirmation as a `.zip`. The confirmation
|
||||
text uses `extractLargeAsk` (unchanged from v1.7) — the wording is
|
||||
format-agnostic, so no new strings are needed.
|
||||
text uses `extractLargeAsk` (slightly relaxed in v1.8.1) — the wording
|
||||
is format-agnostic, so no new strings are needed.
|
||||
|
||||
---
|
||||
|
||||
@@ -441,7 +441,7 @@ host has any RAR tooling.
|
||||
| `test_engine_dispatch_null_dst` | `rar_extract(path, NULL, …)` is rejected with `ZIPX_ERR_INTERNAL`. |
|
||||
| `test_engine_dispatch_dst_is_regular_file` | `rar_extract(path, /some/file, …)` returns `ZIPX_ERR_CONFLICT` (open_parent_dirs fails). |
|
||||
| `test_format_translation` | Parametric: for each `DMC_UNRAR_*` code we care about, the corresponding `rar_translate_error()` mapping is exercised indirectly (via `result->message` strings). |
|
||||
| `test_limits_handoff_default` | When `task->extract_large == 0`, the default profile is handed in (200 K entries / 512 GiB / 64 GiB / 200:1). |
|
||||
| `test_limits_handoff_default` | When `task->extract_large == 0`, the default profile is handed in (200 K entries / 1 TiB / 256 GiB / 500:1). |
|
||||
| `test_limits_handoff_large` | When `task->extract_large == 1`, the large profile is handed in (500 K / 2 TiB / 1 TiB / 1000:1). |
|
||||
| `test_translate_open_fail_to_err_open` | DMC open-failure → `ZIPX_ERR_OPEN`. |
|
||||
| `test_translate_volume_unsp_to_err_unsupported` | The DMC volume code → `ZIPX_ERR_UNSUPPORTED`. |
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* PS5-only stub for the libgcc CPU model symbols.
|
||||
*
|
||||
* unrar's rijndael.cpp / system.cpp call __builtin_cpu_supports() to pick
|
||||
* AES-NI fast paths. Clang lowers that to a reference on __cpu_model (data)
|
||||
* and __cpu_indicator_init() (function), which the FreeBSD-style PS5
|
||||
* sysroot does not provide (no libgcc). This TU supplies both so the link
|
||||
* succeeds; the detection result is unused because we always build the
|
||||
* portable C path.
|
||||
*
|
||||
* Do NOT add this file to host/linux builds: libstdc++/libgcc already
|
||||
* define __cpu_model there and the symbols would collide. */
|
||||
#if defined(__x86_64__) && !defined(__linux__) && !defined(_WIN32)
|
||||
|
||||
struct __cpu_model {
|
||||
int __cpu_vendor;
|
||||
int __cpu_type;
|
||||
int __cpu_subtype;
|
||||
};
|
||||
|
||||
int __cpu_indicator_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct __cpu_model __cpu_model = { 0, 0, 0 };
|
||||
|
||||
#endif
|
||||
+22
-20
@@ -45,6 +45,28 @@ ends_with_ci(const char *path, const char *suffix) {
|
||||
return !strcasecmp(path + path_len - suf_len, suffix);
|
||||
}
|
||||
|
||||
/* Progress callback. The engine already throttles reports (200 ms / 1 MiB),
|
||||
so we can forward each report straight into the shared task state.
|
||||
Defined before extract_dispatch() so the dispatcher's call site compiles
|
||||
cleanly under -Werror=implicit-function-declaration. */
|
||||
static void
|
||||
extract_progress(void *userdata, const zipx_progress_t *p) {
|
||||
file_task_t *task = userdata;
|
||||
unsigned long long prev_done;
|
||||
unsigned long long delta;
|
||||
|
||||
pthread_mutex_lock(&g_tasks_lock);
|
||||
task->entries_total = p->entries_total;
|
||||
task->entries_done = p->entries_done;
|
||||
task->total = p->bytes_total;
|
||||
prev_done = task->done;
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
|
||||
delta = p->bytes_done > prev_done ? p->bytes_done - prev_done : 0;
|
||||
task_update(task, TASK_RUNNING, p->current ? p->current : task->src,
|
||||
delta, NULL);
|
||||
}
|
||||
|
||||
/* Pick the right engine by the archive file name. Returns ZIPX_ERR_FORMAT
|
||||
for anything that does not look like a supported archive. */
|
||||
static zipx_status_t
|
||||
@@ -71,26 +93,6 @@ extract_dispatch(file_task_t *task, zipx_conflict_t conflict,
|
||||
}
|
||||
}
|
||||
|
||||
/* Progress callback. The engine already throttles reports (200 ms / 1 MiB),
|
||||
so we can forward each report straight into the shared task state. */
|
||||
static void
|
||||
extract_progress(void *userdata, const zipx_progress_t *p) {
|
||||
file_task_t *task = userdata;
|
||||
unsigned long long prev_done;
|
||||
unsigned long long delta;
|
||||
|
||||
pthread_mutex_lock(&g_tasks_lock);
|
||||
task->entries_total = p->entries_total;
|
||||
task->entries_done = p->entries_done;
|
||||
task->total = p->bytes_total;
|
||||
prev_done = task->done;
|
||||
pthread_mutex_unlock(&g_tasks_lock);
|
||||
|
||||
delta = p->bytes_done > prev_done ? p->bytes_done - prev_done : 0;
|
||||
task_update(task, TASK_RUNNING, p->current ? p->current : task->src,
|
||||
delta, NULL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
extract_error_code(zipx_status_t status) {
|
||||
switch(status) {
|
||||
|
||||
+197
-360
@@ -1,18 +1,26 @@
|
||||
/* Safe RAR extraction engine used by the /api/extract task.
|
||||
Wraps the vendored dmc_unrar library (https://github.com/DrMcCoy/dmc_unrar).
|
||||
Wraps the vendored rarlab UnRAR 7.x library (third_party/unrar7) through
|
||||
its C-compatible DLL API (dll.hpp / unrar_c_api.h facade).
|
||||
|
||||
The publish / staging / rollback / normalize-name / dedup machinery is
|
||||
mirrored from zip_extract.c so that any consumer of zipx_result_t gets a
|
||||
consistent error and progress contract regardless of archive format.
|
||||
|
||||
See third_party/unrar/VENDORED.md for what dmc_unrar does and does not
|
||||
support (single-volume RAR only; multi-volume and encrypted are
|
||||
rejected up front). */
|
||||
Backend notes (v1.9, unrar 7.20.1):
|
||||
* RAR4 and RAR5 (any compression version, incl. WinRAR 6/7 "v6")
|
||||
single-volume archives.
|
||||
* Multi-volume archives: unrar auto-merges subsequent volumes by name
|
||||
pattern when all .partNN.rar files sit next to the opened volume.
|
||||
* Encrypted RAR: the engine can decrypt via RARSetPassword, but the
|
||||
password plumbing (API + UI) is not wired yet — encrypted archives
|
||||
currently fail with ZIPX_ERR_UNSUPPORTED.
|
||||
|
||||
See third_party/unrar7/VENDORED.md for the full integration notes. */
|
||||
|
||||
#include "rar_extract.h"
|
||||
#include "zip_extract.h" /* for the shared status / progress / limits API */
|
||||
|
||||
#include "dmc_unrar_api.h" /* facade — links against dmc_unrar.o at build time */
|
||||
#include "unrar_c_api.h" /* facade — links against the unrar7 static lib at build time */
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@@ -28,11 +36,11 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* (intentionally no `#include "dmc_unrar.c"` here — that would pull the
|
||||
library into this translation unit, where the host test build's
|
||||
tests/posix_compat.h renames `open` / `close` to `wfm_open` / `wfm_close`
|
||||
and breaks dmc_unrar's `dmc_unrar_io_handler` struct member access.
|
||||
dmc_unrar.c is compiled as its own translation unit and joined at link.) */
|
||||
/* (intentionally no `#include "rar.hpp"` etc. here — those are C++ headers.
|
||||
rar_extract.c talks to unrar exclusively through the extern "C" DLL API in
|
||||
dll.hpp, and the unrar sources are compiled as their own translation units
|
||||
(RARDLL mode) and joined at link time. This keeps the host test build's
|
||||
tests/posix_compat.h renames (open->wfm_open) from leaking into unrar.) */
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
@@ -288,7 +296,7 @@ nameset_put(rarx_nameset_t *set, uint64_t h, int is_dir) {
|
||||
/**************************************************************************
|
||||
* entry name validation (mirrors zip_extract.c::normalize_name but the
|
||||
* RAR side does not have a trailing separator, so dir entries are only
|
||||
* detected via dmc_unrar_file_is_directory()).
|
||||
* detected via the RHDF_DIRECTORY header flag).
|
||||
**************************************************************************/
|
||||
|
||||
static int
|
||||
@@ -300,7 +308,11 @@ normalize_name(const char *in, char *out, size_t out_size, int *is_dir,
|
||||
size_t seg_len = 0;
|
||||
uint32_t levels = 0;
|
||||
|
||||
*is_dir = 0;
|
||||
/* NOTE: is_dir is owned by the caller — unrar announces directories via
|
||||
the RHDF_DIRECTORY header flag, not via a trailing separator, so we
|
||||
must NOT clear it here (a stray `*is_dir = 0` previously turned every
|
||||
directory entry into a file and tripped the duplicate detector when an
|
||||
explicit directory header followed files beneath it). */
|
||||
*depth = 0;
|
||||
if(!in_len) {
|
||||
return rarx_fail(c, ZIPX_ERR_UNSAFE_NAME, in, "empty entry name");
|
||||
@@ -422,77 +434,46 @@ nameset_check_duplicate(rarx_nameset_t *set, const char *name, int is_dir,
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* dmc_unrar -> zipx error translation
|
||||
* unrar DLL error -> zipx error translation
|
||||
**************************************************************************/
|
||||
|
||||
static int
|
||||
rar_translate_error(dmc_unrar_return code, const char *detail,
|
||||
rarx_ctx_t *c) {
|
||||
rar_translate_error(int code, const char *detail, rarx_ctx_t *c) {
|
||||
switch(code) {
|
||||
case DMC_UNRAR_OK:
|
||||
case ERAR_SUCCESS:
|
||||
return ZIPX_OK;
|
||||
|
||||
/* Archive-open level (the user will see this when they upload a
|
||||
multi-volume or fully-encrypted RAR — let them know it is on purpose). */
|
||||
case DMC_UNRAR_ARCHIVE_UNSUPPORTED_VOLUMES:
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"multi-volume RAR archives are not supported; "
|
||||
"please extract on a PC first");
|
||||
/* The unrar engine handles multi-volume automatically (it merges the
|
||||
next .partNN.rar by name); a missing next volume surfaces as EOPEN. */
|
||||
case ERAR_EOPEN:
|
||||
return rarx_fail(c, ZIPX_ERR_OPEN, detail, "%s", strerror(errno));
|
||||
|
||||
case DMC_UNRAR_ARCHIVE_UNSUPPORTED_ENCRYPTED:
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"encrypted RAR archives are not supported; "
|
||||
"please extract on a PC first");
|
||||
case ERAR_ECREATE:
|
||||
case ERAR_ECLOSE:
|
||||
case ERAR_EREAD:
|
||||
case ERAR_EWRITE:
|
||||
return rarx_fail(c, ZIPX_ERR_IO, detail, "%s", strerror(errno));
|
||||
|
||||
/* Per-file (still surfaced up-front during scan). */
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_ENCRYPTED:
|
||||
case ERAR_MISSING_PASSWORD:
|
||||
case ERAR_BAD_PASSWORD:
|
||||
/* Password plumbing (API + UI) is not wired yet. */
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"encrypted RAR entries are not supported");
|
||||
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_SPLIT:
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"split RAR entries are not supported");
|
||||
case ERAR_SMALL_BUF:
|
||||
return rarx_fail(c, ZIPX_ERR_LIMIT_NAME, detail, "name buffer is too small");
|
||||
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_LINK:
|
||||
return rarx_fail(c, ZIPX_ERR_SPECIAL, detail, "symbolic link entry");
|
||||
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_VERSION:
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_METHOD:
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"RAR entry uses an unsupported compression method");
|
||||
|
||||
case DMC_UNRAR_FILE_UNSUPPORTED_LARGE:
|
||||
case ERAR_LARGE_DICT:
|
||||
return rarx_fail(c, ZIPX_ERR_LIMIT_FILE, detail,
|
||||
"RAR entry is larger than the supported maximum");
|
||||
"archive needs a larger dictionary than supported");
|
||||
|
||||
case DMC_UNRAR_ARCHIVE_EMPTY:
|
||||
return rarx_fail(c, ZIPX_ERR_FORMAT, detail, "empty RAR archive");
|
||||
case ERAR_BAD_DATA:
|
||||
return rarx_fail(c, ZIPX_ERR_CRC, detail, "checksum mismatch in entry data");
|
||||
|
||||
case DMC_UNRAR_ARCHIVE_UNSUPPORTED_ANCIENT:
|
||||
return rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail,
|
||||
"RAR 1.4 / 1.5 archives are not supported");
|
||||
|
||||
case DMC_UNRAR_ARCHIVE_NOT_RAR:
|
||||
return rarx_fail(c, ZIPX_ERR_FORMAT, detail,
|
||||
"not a RAR archive");
|
||||
|
||||
case DMC_UNRAR_OPEN_FAIL:
|
||||
return rarx_fail(c, ZIPX_ERR_OPEN, detail, "%s", strerror(errno));
|
||||
|
||||
case DMC_UNRAR_READ_FAIL:
|
||||
case DMC_UNRAR_WRITE_FAIL:
|
||||
case DMC_UNRAR_SEEK_FAIL:
|
||||
return rarx_fail(c, ZIPX_ERR_IO, detail, "%s", strerror(errno));
|
||||
|
||||
case DMC_UNRAR_FILE_CRC32_FAIL:
|
||||
return rarx_fail(c, ZIPX_ERR_CRC, detail, "CRC32 mismatch");
|
||||
|
||||
case DMC_UNRAR_INVALID_DATA:
|
||||
case DMC_UNRAR_NO_ALLOC:
|
||||
case DMC_UNRAR_ALLOC_FAIL:
|
||||
case DMC_UNRAR_ARCHIVE_IS_NULL:
|
||||
case DMC_UNRAR_ARCHIVE_NOT_CLEARED:
|
||||
case DMC_UNRAR_ARCHIVE_MISSING_FIELDS:
|
||||
case ERAR_BAD_ARCHIVE:
|
||||
case ERAR_UNKNOWN_FORMAT:
|
||||
case ERAR_UNKNOWN:
|
||||
case ERAR_NO_MEMORY:
|
||||
default:
|
||||
return rarx_fail(c, ZIPX_ERR_FORMAT, detail,
|
||||
"invalid or corrupt RAR archive");
|
||||
@@ -532,15 +513,6 @@ path_parent(const char *path, char *out, size_t out_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
chmod_0777_fd(int fd) {
|
||||
int err = errno;
|
||||
|
||||
if(fchmod(fd, 0777)) {
|
||||
errno = err;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
make_staging(rarx_ctx_t *c, const char *parent) {
|
||||
unsigned int attempt;
|
||||
@@ -617,57 +589,6 @@ rollback_published(rarx_ctx_t *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* mkdir -p: ensures every segment below root_fd exists. Returns the open
|
||||
descriptor for the deepest directory. */
|
||||
static int
|
||||
open_parent_dirs(int root_fd, const char *rel, rarx_ctx_t *c) {
|
||||
char buf[ZIPX_PATH_MAX];
|
||||
int fd = root_fd;
|
||||
char *seg;
|
||||
char *save = NULL;
|
||||
|
||||
if(strlen(rel) >= sizeof(buf)) {
|
||||
rarx_fail(c, ZIPX_ERR_LIMIT_NAME, rel, "path is too long");
|
||||
return -1;
|
||||
}
|
||||
strcpy(buf, rel);
|
||||
|
||||
for(seg = strtok_r(buf, "/", &save); seg; seg = strtok_r(NULL, "/", &save)) {
|
||||
int next;
|
||||
|
||||
if(!mkdirat(fd, seg, 0777)) {
|
||||
next = openat(fd, seg, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if(next >= 0) {
|
||||
chmod_0777_fd(next);
|
||||
c->dirs_created++;
|
||||
}
|
||||
} else if(errno == EEXIST) {
|
||||
next = openat(fd, seg, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
} else {
|
||||
rarx_fail(c, ZIPX_ERR_IO, rel, "cannot create directory '%s': %s", seg,
|
||||
strerror(errno));
|
||||
if(fd != root_fd) {
|
||||
close(fd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(next < 0) {
|
||||
rarx_fail(c, ZIPX_ERR_IO, rel, "cannot open directory '%s': %s", seg,
|
||||
strerror(errno));
|
||||
if(fd != root_fd) {
|
||||
close(fd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if(fd != root_fd) {
|
||||
close(fd);
|
||||
}
|
||||
fd = next;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* scan phase
|
||||
**************************************************************************/
|
||||
@@ -695,74 +616,70 @@ check_space(rarx_ctx_t *c, const char *target) {
|
||||
}
|
||||
|
||||
static int
|
||||
scan_archive(dmc_unrar_archive *rar, rarx_ctx_t *c) {
|
||||
scan_archive(HANDLE hArc, rarx_ctx_t *c) {
|
||||
rarx_nameset_t set;
|
||||
dmc_unrar_size_t i;
|
||||
dmc_unrar_size_t total;
|
||||
int ret = 0;
|
||||
|
||||
if(nameset_init(&set, 4096)) {
|
||||
return rarx_fail(c, ZIPX_ERR_INTERNAL, NULL, "out of memory");
|
||||
}
|
||||
|
||||
total = dmc_unrar_get_file_count(rar);
|
||||
for(i = 0; i < total; i++) {
|
||||
for(;;) {
|
||||
struct RARHeaderDataEx hdr;
|
||||
uint64_t uncomp;
|
||||
char name[ZIPX_PATH_MAX];
|
||||
char *name_buf;
|
||||
dmc_unrar_size_t name_size;
|
||||
int is_dir;
|
||||
uint32_t depth = 0;
|
||||
const dmc_unrar_file *info;
|
||||
dmc_unrar_return supported;
|
||||
int rc;
|
||||
|
||||
info = dmc_unrar_get_file_stat(rar, i);
|
||||
if(!info) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "cannot read entry header");
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
rc = RARReadHeaderEx(hArc, &hdr);
|
||||
if(rc == ERAR_END_ARCHIVE) {
|
||||
break;
|
||||
}
|
||||
supported = dmc_unrar_file_is_supported(rar, i);
|
||||
if(supported != DMC_UNRAR_OK) {
|
||||
/* is_supported() already returns ZIPX-mapped error via a temporary */
|
||||
char detail[64];
|
||||
snprintf(detail, sizeof(detail), "entry %llu",
|
||||
(unsigned long long)i);
|
||||
rarx_fail(c, ZIPX_ERR_UNSUPPORTED, detail, "%s",
|
||||
dmc_unrar_strerror(supported));
|
||||
ret = (int)c->result->status;
|
||||
break;
|
||||
}
|
||||
is_dir = dmc_unrar_file_is_directory(rar, i) ? 1 : 0;
|
||||
|
||||
/* First call to learn the required size, second to fill the buffer. */
|
||||
name_size = dmc_unrar_get_filename(rar, i, NULL, 0);
|
||||
if(!name_size) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL,
|
||||
"cannot read entry name");
|
||||
break;
|
||||
}
|
||||
name_buf = malloc(name_size);
|
||||
if(!name_buf) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_INTERNAL, NULL, "out of memory");
|
||||
break;
|
||||
}
|
||||
if(dmc_unrar_get_filename(rar, i, name_buf, name_size) != name_size) {
|
||||
free(name_buf);
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL,
|
||||
"truncated entry name");
|
||||
break;
|
||||
}
|
||||
/* dmc_unrar does not guarantee NUL termination; force it. */
|
||||
name_buf[name_size - 1] = 0;
|
||||
/* RAR stores UTF-8 encoded names. Replace any non-UTF-8 sequence with
|
||||
'?' to keep the downstream string well-formed. */
|
||||
dmc_unrar_unicode_make_valid_utf8(name_buf);
|
||||
|
||||
if(normalize_name(name_buf, name, sizeof(name), &is_dir, &depth, c)) {
|
||||
free(name_buf);
|
||||
if(rc != ERAR_SUCCESS) {
|
||||
rar_translate_error(rc, NULL, c);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
free(name_buf);
|
||||
|
||||
/* unrar hands out the header name as a NUL-terminated string
|
||||
(UTF-8 on the PS5 / POSIX build). */
|
||||
if(hdr.FileName[0] == 0) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "empty entry name");
|
||||
break;
|
||||
}
|
||||
is_dir = (hdr.Flags & RHDF_DIRECTORY) ? 1 : 0;
|
||||
|
||||
/* Encrypted entries: the unrar engine can decrypt them via RARSetPassword,
|
||||
but the password plumbing is not wired yet — reject up front with the
|
||||
same message the v1.8 backend used. */
|
||||
if(hdr.Flags & RHDF_ENCRYPTED) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_UNSUPPORTED, hdr.FileName,
|
||||
"encrypted RAR entries are not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Multi-volume: a file spanning volumes is presented as several header
|
||||
segments with the SAME name. Segments after the first carry
|
||||
RHDF_SPLITBEFORE; they must drive the engine forward (SKIP) but must
|
||||
not be counted, deduped or size-accumulated again. The first segment
|
||||
already carries the full file size. */
|
||||
if(hdr.Flags & RHDF_SPLITBEFORE) {
|
||||
rc = RARProcessFile(hArc, RAR_SKIP, NULL, NULL);
|
||||
if(rc != ERAR_SUCCESS && rc != ERAR_END_ARCHIVE) {
|
||||
ret = -1;
|
||||
rar_translate_error(rc, hdr.FileName, c);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(normalize_name(hdr.FileName, name, sizeof(name), &is_dir, &depth, c)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
(void)depth;
|
||||
|
||||
if(nameset_check_duplicate(&set, name, is_dir, c) ||
|
||||
nameset_add_path(&set, name, is_dir, c)) {
|
||||
@@ -771,7 +688,7 @@ scan_archive(dmc_unrar_archive *rar, rarx_ctx_t *c) {
|
||||
}
|
||||
|
||||
if(!is_dir) {
|
||||
uint64_t uncomp = info->uncompressed_size;
|
||||
uncomp = (uint64_t)hdr.UnpSizeHigh << 32 | hdr.UnpSize;
|
||||
|
||||
if(uncomp > c->limits.max_file_bytes) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_LIMIT_FILE, name,
|
||||
@@ -779,8 +696,6 @@ scan_archive(dmc_unrar_archive *rar, rarx_ctx_t *c) {
|
||||
(unsigned long long)c->limits.max_file_bytes);
|
||||
break;
|
||||
}
|
||||
/* RAR headers do not reliably expose compressed_size for all formats,
|
||||
so the safe compression-ratio check is skipped. */
|
||||
if(uncomp >= c->limits.max_total_bytes ||
|
||||
c->bytes_total > c->limits.max_total_bytes - uncomp) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_LIMIT_TOTAL, name,
|
||||
@@ -805,6 +720,17 @@ scan_archive(dmc_unrar_archive *rar, rarx_ctx_t *c) {
|
||||
}
|
||||
report(c, ZIPX_PHASE_SCAN, name, 0);
|
||||
}
|
||||
|
||||
/* Advance to the next file header (also drives multi-volume merges). */
|
||||
rc = RARProcessFile(hArc, RAR_SKIP, NULL, NULL);
|
||||
if(rc != ERAR_SUCCESS) {
|
||||
if(rc != ERAR_END_ARCHIVE) {
|
||||
ret = -1;
|
||||
rar_translate_error(rc, name, c);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nameset_free(&set);
|
||||
@@ -815,175 +741,72 @@ scan_archive(dmc_unrar_archive *rar, rarx_ctx_t *c) {
|
||||
* extract phase
|
||||
**************************************************************************/
|
||||
|
||||
/* dmc_unrar_extract_file_to_path() opens the destination file with fopen()
|
||||
without creating parent directories. We always call it against an
|
||||
already-prepared full path inside the staging tree. */
|
||||
/* unrar extracts each entry directly under the staging root and creates
|
||||
parent directories itself. All entry names were validated (normalize_name)
|
||||
during scan, so what lands in the staging tree is safe by construction. */
|
||||
static int
|
||||
extract_one(dmc_unrar_archive *rar, rarx_ctx_t *c, int root_fd,
|
||||
const char *name, dmc_unrar_size_t index, uint64_t declared) {
|
||||
char *full_path;
|
||||
char dir_part[ZIPX_PATH_MAX];
|
||||
char base[ZIPX_PATH_MAX];
|
||||
char *slash;
|
||||
int dir_fd;
|
||||
dmc_unrar_return dr;
|
||||
struct stat st;
|
||||
uint64_t before_bytes;
|
||||
extract_archive(HANDLE hArc, rarx_ctx_t *c) {
|
||||
int ret = 0;
|
||||
|
||||
snprintf(dir_part, sizeof(dir_part), "%s", name);
|
||||
slash = strrchr(dir_part, '/');
|
||||
if(slash) {
|
||||
snprintf(base, sizeof(base), "%s", slash + 1);
|
||||
*slash = 0;
|
||||
} else {
|
||||
snprintf(base, sizeof(base), "%s", name);
|
||||
dir_part[0] = 0;
|
||||
}
|
||||
if(!base[0]) {
|
||||
return rarx_fail(c, ZIPX_ERR_UNSAFE_NAME, name, "empty file name");
|
||||
}
|
||||
|
||||
dir_fd = open_parent_dirs(root_fd, dir_part, c);
|
||||
if(dir_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
full_path = malloc(ZIPX_PATH_MAX);
|
||||
if(!full_path) {
|
||||
rarx_fail(c, ZIPX_ERR_INTERNAL, name, "out of memory");
|
||||
if(dir_fd != root_fd) {
|
||||
close(dir_fd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if(dir_part[0]) {
|
||||
snprintf(full_path, ZIPX_PATH_MAX, "%s/%s/%s",
|
||||
c->staging, dir_part, base);
|
||||
} else {
|
||||
snprintf(full_path, ZIPX_PATH_MAX, "%s/%s", c->staging, base);
|
||||
}
|
||||
|
||||
before_bytes = c->bytes_done;
|
||||
dr = dmc_unrar_extract_file_to_path(rar, index, full_path, NULL, true);
|
||||
if(dr != DMC_UNRAR_OK) {
|
||||
rar_translate_error(dr, name, c);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(stat(full_path, &st)) {
|
||||
rarx_fail(c, ZIPX_ERR_IO, name, "cannot stat extracted file: %s",
|
||||
strerror(errno));
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
if((uint64_t)st.st_size > declared + declared + (64 * 1024)) {
|
||||
/* Defensive: the on-disk size should never wildly exceed the declared
|
||||
size. The cap is generous so PPMd + headers do not trip it. */
|
||||
rarx_fail(c, ZIPX_ERR_LIMIT_FILE, name,
|
||||
"extracted file is larger than declared");
|
||||
unlink(full_path);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
if((uint64_t)st.st_size > c->limits.max_total_bytes - c->bytes_done) {
|
||||
rarx_fail(c, ZIPX_ERR_LIMIT_TOTAL, name,
|
||||
"archive contents are larger than %llu bytes",
|
||||
(unsigned long long)c->limits.max_total_bytes);
|
||||
unlink(full_path);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
c->bytes_done += (uint64_t)st.st_size;
|
||||
/* If dmc_unrar did not advance bytes_done above (e.g. declared == 0 for a
|
||||
directory or zero-byte file), credit the bytes_total estimate so the
|
||||
progress bar keeps moving. */
|
||||
if(c->bytes_done == before_bytes && declared) {
|
||||
c->bytes_done += declared;
|
||||
}
|
||||
|
||||
done:
|
||||
free(full_path);
|
||||
if(dir_fd != root_fd) {
|
||||
close(dir_fd);
|
||||
}
|
||||
if(!ret) {
|
||||
c->files_created++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
extract_archive(dmc_unrar_archive *rar, rarx_ctx_t *c, int root_fd) {
|
||||
dmc_unrar_size_t i;
|
||||
dmc_unrar_size_t total = dmc_unrar_get_file_count(rar);
|
||||
int ret = 0;
|
||||
|
||||
for(i = 0; i < total && !ret; i++) {
|
||||
char *name_buf = NULL;
|
||||
dmc_unrar_size_t name_size;
|
||||
char name[ZIPX_PATH_MAX];
|
||||
for(;;) {
|
||||
struct RARHeaderDataEx hdr;
|
||||
int rc;
|
||||
int is_dir;
|
||||
uint32_t depth = 0;
|
||||
const dmc_unrar_file *info;
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
rc = RARReadHeaderEx(hArc, &hdr);
|
||||
if(rc == ERAR_END_ARCHIVE) {
|
||||
break;
|
||||
}
|
||||
if(rc != ERAR_SUCCESS) {
|
||||
rar_translate_error(rc, NULL, c);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(canceled(c)) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_CANCELED, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
if(hdr.FileName[0] == 0) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "empty entry name");
|
||||
break;
|
||||
}
|
||||
if(hdr.Flags & RHDF_ENCRYPTED) {
|
||||
/* scan already rejected these; defensive only. */
|
||||
ret = rarx_fail(c, ZIPX_ERR_UNSUPPORTED, hdr.FileName,
|
||||
"encrypted RAR entries are not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
info = dmc_unrar_get_file_stat(rar, i);
|
||||
if(!info) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "cannot read entry header");
|
||||
break;
|
||||
is_dir = (hdr.Flags & RHDF_DIRECTORY) ? 1 : 0;
|
||||
/* Split continuation segments still need RARProcessFile(EXTRACT) so the
|
||||
volume chain is driven and the file is completed, but only the first
|
||||
segment is counted / reported / size-accumulated. */
|
||||
if(hdr.Flags & RHDF_SPLITBEFORE) {
|
||||
rc = RARProcessFile(hArc, RAR_EXTRACT, c->staging, NULL);
|
||||
if(rc != ERAR_SUCCESS) {
|
||||
rar_translate_error(rc, hdr.FileName, c);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
is_dir = dmc_unrar_file_is_directory(rar, i) ? 1 : 0;
|
||||
|
||||
name_size = dmc_unrar_get_filename(rar, i, NULL, 0);
|
||||
if(!name_size) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "cannot read entry name");
|
||||
break;
|
||||
}
|
||||
name_buf = malloc(name_size);
|
||||
if(!name_buf) {
|
||||
ret = rarx_fail(c, ZIPX_ERR_INTERNAL, NULL, "out of memory");
|
||||
break;
|
||||
}
|
||||
if(dmc_unrar_get_filename(rar, i, name_buf, name_size) != name_size) {
|
||||
free(name_buf);
|
||||
ret = rarx_fail(c, ZIPX_ERR_FORMAT, NULL, "truncated entry name");
|
||||
break;
|
||||
}
|
||||
name_buf[name_size - 1] = 0;
|
||||
dmc_unrar_unicode_make_valid_utf8(name_buf);
|
||||
|
||||
if(normalize_name(name_buf, name, sizeof(name), &is_dir, &depth, c)) {
|
||||
free(name_buf);
|
||||
rc = RARProcessFile(hArc, RAR_EXTRACT, c->staging, NULL);
|
||||
if(rc != ERAR_SUCCESS) {
|
||||
rar_translate_error(rc, hdr.FileName, c);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
free(name_buf);
|
||||
(void)depth;
|
||||
|
||||
if(is_dir) {
|
||||
int dir_fd = open_parent_dirs(root_fd, name, c);
|
||||
if(dir_fd < 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
if(dir_fd != root_fd) {
|
||||
close(dir_fd);
|
||||
}
|
||||
c->dirs_created++;
|
||||
} else {
|
||||
uint64_t declared = info->uncompressed_size;
|
||||
if(extract_one(rar, c, root_fd, name, i, declared)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
c->files_created++;
|
||||
c->bytes_done += (uint64_t)hdr.UnpSizeHigh << 32 | hdr.UnpSize;
|
||||
}
|
||||
c->entries_done++;
|
||||
report(c, ZIPX_PHASE_EXTRACT, name, 0);
|
||||
report(c, ZIPX_PHASE_EXTRACT, hdr.FileName, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1151,9 +974,7 @@ rar_extract(const char *rar_path, const char *dst_dir,
|
||||
char parent[ZIPX_PATH_MAX];
|
||||
char dst_copy[ZIPX_PATH_MAX];
|
||||
struct stat st;
|
||||
dmc_unrar_archive rar;
|
||||
dmc_unrar_return dr;
|
||||
int root_fd = -1;
|
||||
HANDLE hArc = NULL;
|
||||
int dst_existed = 0;
|
||||
int status;
|
||||
|
||||
@@ -1197,50 +1018,67 @@ rar_extract(const char *rar_path, const char *dst_dir,
|
||||
goto done;
|
||||
}
|
||||
|
||||
dr = dmc_unrar_archive_init(&rar);
|
||||
if(dr != DMC_UNRAR_OK) {
|
||||
status = rarx_fail(c, ZIPX_ERR_INTERNAL, rar_path,
|
||||
"cannot initialize RAR decoder");
|
||||
goto done;
|
||||
}
|
||||
dr = dmc_unrar_archive_open_path(&rar, rar_path);
|
||||
if(dr != DMC_UNRAR_OK) {
|
||||
status = rar_translate_error(dr, rar_path, c);
|
||||
goto done;
|
||||
/* Open with RAR_OM_EXTRACT for both passes: the unrar engine drives
|
||||
multi-volume merges identically while scanning (skip) and extracting,
|
||||
and encrypted headers surface here as a missing/bad password. */
|
||||
{
|
||||
struct RAROpenArchiveDataEx od;
|
||||
memset(&od, 0, sizeof(od));
|
||||
od.ArcName = (char *)rar_path;
|
||||
od.OpenMode = RAR_OM_EXTRACT;
|
||||
hArc = RAROpenArchiveEx(&od);
|
||||
if(!hArc) {
|
||||
status = rar_translate_error((int)od.OpenResult, rar_path, c);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if(scan_archive(&rar, c)) {
|
||||
if(scan_archive(hArc, c)) {
|
||||
status = (int)c->result->status;
|
||||
RARCloseArchive(hArc);
|
||||
hArc = NULL;
|
||||
goto done;
|
||||
}
|
||||
if(canceled(c)) {
|
||||
status = rarx_fail(c, ZIPX_ERR_CANCELED, NULL, NULL);
|
||||
RARCloseArchive(hArc);
|
||||
hArc = NULL;
|
||||
goto done;
|
||||
}
|
||||
if(check_space(c, dst_existed ? dst_copy : parent)) {
|
||||
status = (int)c->result->status;
|
||||
RARCloseArchive(hArc);
|
||||
hArc = NULL;
|
||||
goto done;
|
||||
}
|
||||
if(make_staging(c, parent)) {
|
||||
status = (int)c->result->status;
|
||||
RARCloseArchive(hArc);
|
||||
hArc = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
root_fd = open(c->staging, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if(root_fd < 0) {
|
||||
status = rarx_fail(c, ZIPX_ERR_IO, c->staging, "cannot open staging: %s",
|
||||
strerror(errno));
|
||||
goto done;
|
||||
/* Pass 2: re-open and extract each entry straight into the staging tree
|
||||
(unrar creates parent directories itself; names were validated during
|
||||
scan). The staging root is pre-verified below for fast failure. */
|
||||
RARCloseArchive(hArc);
|
||||
hArc = NULL;
|
||||
{
|
||||
struct RAROpenArchiveDataEx od;
|
||||
memset(&od, 0, sizeof(od));
|
||||
od.ArcName = (char *)rar_path;
|
||||
od.OpenMode = RAR_OM_EXTRACT;
|
||||
hArc = RAROpenArchiveEx(&od);
|
||||
if(!hArc) {
|
||||
status = rar_translate_error((int)od.OpenResult, rar_path, c);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if(extract_archive(&rar, c, root_fd)) {
|
||||
if(extract_archive(hArc, c)) {
|
||||
status = (int)c->result->status;
|
||||
goto done;
|
||||
}
|
||||
if(root_fd >= 0) {
|
||||
close(root_fd);
|
||||
root_fd = -1;
|
||||
}
|
||||
if(canceled(c)) {
|
||||
status = rarx_fail(c, ZIPX_ERR_CANCELED, NULL, NULL);
|
||||
goto done;
|
||||
@@ -1254,11 +1092,10 @@ rar_extract(const char *rar_path, const char *dst_dir,
|
||||
status = ZIPX_OK;
|
||||
|
||||
done:
|
||||
if(root_fd >= 0) {
|
||||
close(root_fd);
|
||||
if(hArc) {
|
||||
RARCloseArchive(hArc);
|
||||
}
|
||||
cleanup_staging(c);
|
||||
dmc_unrar_archive_close(&rar);
|
||||
result->entries_total = c->entries_total;
|
||||
result->entries_done = c->entries_done;
|
||||
result->bytes_total = c->bytes_total;
|
||||
|
||||
+9
-8
@@ -1,21 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
/* Safe RAR extraction engine used by the /api/extract task.
|
||||
Wraps the vendored dmc_unrar library (https://github.com/DrMcCoy/dmc_unrar).
|
||||
Wraps the vendored rarlab UnRAR 7.20.1 (third_party/unrar7) through its
|
||||
C-compatible DLL API (unrar_c_api.h facade).
|
||||
|
||||
Reuses the zip_extract types so the dispatch layer can call either engine
|
||||
through the same status / limits / progress protocol.
|
||||
|
||||
See zip_extract.h for the shared limits, conflict, progress and result types.
|
||||
|
||||
Constraints of v1.8 (dmc_unrar 1.7.0 backend):
|
||||
* Single-volume RAR archives only. Multi-volume (.partNN.rar) archives
|
||||
are rejected with ZIPX_ERR_UNSUPPORTED — extract them on a PC first.
|
||||
* Unencrypted RAR only. Encrypted headers / files are rejected with
|
||||
ZIPX_ERR_UNSUPPORTED. There is no password argument for the same reason.
|
||||
Backend notes (v1.9, unrar 7.20.1):
|
||||
* RAR4 and RAR5, any compression version including WinRAR 6/7 "v6".
|
||||
* Multi-volume: unrar merges next .partNN.rar by name automatically.
|
||||
* Encrypted RAR is NOT yet supported end-to-end: the engine can decrypt
|
||||
via RARSetPassword, but password plumbing (API + UI) is unwired, so
|
||||
encrypted headers/entries fail with ZIPX_ERR_UNSUPPORTED today.
|
||||
|
||||
See third_party/unrar/VENDORED.md for the upgrade path to rarlab UnRAR
|
||||
(which does support both) when / if it becomes worth the C++ integration. */
|
||||
See third_party/unrar7/VENDORED.md for full integration notes. */
|
||||
|
||||
#include "zip_extract.h"
|
||||
|
||||
|
||||
+25
-7
@@ -33,22 +33,40 @@
|
||||
#define ZIPX_PUBLISH_MAX_DEPTH 128
|
||||
#define ZIPX_SPACE_SLACK_PER_ENTRY 512
|
||||
|
||||
/* Default limits.
|
||||
*
|
||||
* Tuned to cover real-world PS5 workloads without prompting:
|
||||
* - PS5 system backup ZIPs (~200-300 GiB total, individual chunks <64 GiB)
|
||||
* - 3A-game archives with a single ~300 GiB uncompressed file
|
||||
*
|
||||
* Safety against zip bombs is delegated to:
|
||||
* 1. `check_space()` (statvfs-based real disk space check) before extract
|
||||
* 2. `max_ratio` below (declared compression ratio cap)
|
||||
* The size caps here are an early-fail UX guard, not a security boundary.
|
||||
*/
|
||||
static const zipx_limits_t k_default_limits = {
|
||||
.max_entries = 200000,
|
||||
.max_total_bytes = 512ULL * 1024 * 1024 * 1024,
|
||||
.max_file_bytes = 64ULL * 1024 * 1024 * 1024,
|
||||
.max_ratio = 200,
|
||||
.max_total_bytes = 2ULL * 1024 * 1024 * 1024 * 1024,
|
||||
.max_file_bytes = 512ULL * 1024 * 1024 * 1024,
|
||||
.max_ratio = 500,
|
||||
.max_depth = 32,
|
||||
.max_name_len = 255,
|
||||
.max_path_len = 1024
|
||||
};
|
||||
|
||||
/* Large profile for archives that exceed the safe limits (e.g. 200 GB system
|
||||
images). The relaxed ratio still rejects obvious bombs; the size caps
|
||||
require the user to opt in via the web UI before they take effect. */
|
||||
/* Large profile for archives that exceed the default cap.
|
||||
*
|
||||
* - max_file_bytes = 1 TiB (single uncompressed file)
|
||||
* - max_total_bytes = 4 TiB (whole archive)
|
||||
* - max_ratio = 1000 (relaxed ratio cap; check_space still applies)
|
||||
*
|
||||
* Requires the user to opt in via the web UI (large=1) before these take
|
||||
* effect. Default limits must always be strictly smaller than large so the
|
||||
* large profile is unambiguously a relaxation.
|
||||
*/
|
||||
static const zipx_limits_t k_large_limits = {
|
||||
.max_entries = 500000,
|
||||
.max_total_bytes = 2ULL * 1024 * 1024 * 1024 * 1024,
|
||||
.max_total_bytes = 4ULL * 1024 * 1024 * 1024 * 1024,
|
||||
.max_file_bytes = 1ULL * 1024 * 1024 * 1024 * 1024,
|
||||
.max_ratio = 1000,
|
||||
.max_depth = 32,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,82 @@
|
||||
@echo off
|
||||
REM Generate real RAR fixtures for the v1.9 host test suite.
|
||||
REM Requires WinRAR command-line Rar.exe (ships with normal WinRAR install).
|
||||
REM Outputs into tests\fixtures-real\ - kept OUT of tests\fixtures\ because
|
||||
REM tests\make_fixtures.py wipes that directory on every test run.
|
||||
REM Rerun any time fixture needs change. NOTE: keep this file pure ASCII.
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set RAREXE=
|
||||
if exist "%ProgramFiles%\WinRAR\Rar.exe" set RAREXE=%ProgramFiles%\WinRAR\Rar.exe
|
||||
if exist "%ProgramFiles(x86)%\WinRAR\Rar.exe" set RAREXE=%ProgramFiles(x86)%\WinRAR\Rar.exe
|
||||
if exist "%~dp0Rar.exe" set RAREXE=%~dp0Rar.exe
|
||||
if "%RAREXE%"=="" (
|
||||
echo [ERROR] Rar.exe not found. Install WinRAR or drop Rar.exe next to this script.
|
||||
exit /b 1
|
||||
)
|
||||
echo Using: %RAREXE%
|
||||
"%RAREXE%" 2>nul | findstr /c:"RAR " >nul || (echo [ERROR] %RAREXE% does not look like WinRAR & exit /b 1)
|
||||
|
||||
set FIX=%~dp0fixtures-real
|
||||
if exist "%FIX%" rmdir /s /q "%FIX%"
|
||||
mkdir "%FIX%"
|
||||
set STAGE=%~dp0fixture-stage
|
||||
if exist "%STAGE%" rmdir /s /q "%STAGE%"
|
||||
mkdir "%STAGE%\dir" 2>nul
|
||||
|
||||
echo ############### > "%STAGE%\root.txt"
|
||||
echo rar v1.9 fixture root content >> "%STAGE%\root.txt"
|
||||
echo nested payload line one > "%STAGE%\dir\nested.txt"
|
||||
echo nested payload line two >> "%STAGE%\dir\nested.txt"
|
||||
echo file-b 0102030405060708090a > "%STAGE%\b.bin"
|
||||
|
||||
REM Work from inside the stage dir with RELATIVE names so the archive keeps
|
||||
REM the dir\ structure (no -ep1 stripping).
|
||||
pushd "%STAGE%"
|
||||
|
||||
echo.
|
||||
echo [1/4] RAR5 v6 single volume - basic-v6.rar
|
||||
"%RAREXE%" a -m2 -ma5 -idq "%FIX%\basic-v6.rar" root.txt dir\nested.txt b.bin
|
||||
if errorlevel 1 echo [FAIL] & goto :badpop
|
||||
|
||||
echo [2/4] RAR5 v6 multi-volume - vol.part1.rar + vol.part2.rar + ...
|
||||
REM Create a 512 KB incompressible payload so -v200k actually splits it.
|
||||
fsutil file createnew big.bin 524288 >nul 2>nul
|
||||
if not exist big.bin (
|
||||
echo [WARN] fsutil unavailable - cannot force multi-volume; skipping
|
||||
goto :aftervol
|
||||
)
|
||||
REM rar a updates an existing archive instead of re-splitting it, so any
|
||||
REM stale volume files must be removed first. Target name is plain
|
||||
REM "vol.rar": RAR5 numbering then yields vol.part1/2/3.rar (naming the
|
||||
REM target vol.part1.rar would double up to vol.part1.partN.rar).
|
||||
del "%FIX%\vol*.rar" 2>nul
|
||||
"%RAREXE%" a -m0 -ma5 -v200k -idq "%FIX%\vol.rar" big.bin root.txt
|
||||
if errorlevel 1 echo [FAIL] & goto :badpop
|
||||
echo wrote: & dir /b "%FIX%\vol.part*.rar" 2>nul
|
||||
:aftervol
|
||||
|
||||
echo [3/4] RAR5 v6 encrypted (password: secret123) - enc-v6.rar
|
||||
"%RAREXE%" a -m2 -ma5 -psecret123 -idq "%FIX%\enc-v6.rar" root.txt dir\nested.txt
|
||||
if errorlevel 1 echo [FAIL] & goto :badpop
|
||||
|
||||
echo [4/4] RAR4 legacy (optional - some WinRAR builds dropped RAR4 writing)
|
||||
"%RAREXE%" a -m2 -ma4 -idq "%FIX%\basic-rar4.rar" root.txt dir\nested.txt
|
||||
if errorlevel 1 (
|
||||
echo [WARN] RAR4 creation not supported by this Rar.exe - skipping basic-rar4.rar
|
||||
) else (
|
||||
echo basic-rar4.rar written
|
||||
)
|
||||
|
||||
popd
|
||||
rmdir /s /q "%STAGE%" 2>nul
|
||||
echo.
|
||||
echo OK. Fixtures written to %FIX%:
|
||||
dir /b "%FIX%\*.rar" 2>nul
|
||||
exit /b 0
|
||||
|
||||
:badpop
|
||||
popd
|
||||
:bad
|
||||
echo [ERROR] WinRAR command failed. Is this WinRAR 5+ with command line support?
|
||||
exit /b 1
|
||||
+29
-9
@@ -39,9 +39,19 @@ for src in "$ROOT"/third_party/zlib/src/*.c "$ROOT"/third_party/minizip-ng/src/*
|
||||
"${MZ_CFLAGS[@]}" "${extra[@]}" -o "$BUILD/$name.o" "$src"
|
||||
done
|
||||
|
||||
# dmc_unrar (single-file; uses stdio fopen by default on non-Windows)
|
||||
"$CC" -c -O2 -w "${RAR_CFLAGS[@]}" \
|
||||
-o "$BUILD/dmc_unrar.o" "$ROOT/third_party/unrar/dmc_unrar.c"
|
||||
# unrar 7.20.1 (RARDLL source set; compiled with the host C++ compiler).
|
||||
UNRAR7_SRCS="$ROOT/third_party/unrar7"
|
||||
UNRAR7_CFLAGS=(-O2 -w -std=c++17 -DRARDLL -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE)
|
||||
CXX="${CXX:-g++}"
|
||||
for src in \
|
||||
archive arcread blake2s cmddata consio crc crypt dll encname errhnd extinfo \
|
||||
extract filcreat file filefn filestr find getbits global hash headers isnt \
|
||||
largepage match motw options pathfn qopen rar rarpch rarvm rawread rdwrfn \
|
||||
rijndael rs rs16 scantree secpassword sha1 sha256 smallfn strfn strlist \
|
||||
system threadpool timefn ui unicode unpack volume; do
|
||||
"$CXX" -c "${UNRAR7_CFLAGS[@]}" -o "$BUILD/unrar7_$src.o" \
|
||||
"$UNRAR7_SRCS/$src.cpp" || exit 1
|
||||
done
|
||||
|
||||
COMPAT_INC="$ROOT/tests/compat"
|
||||
|
||||
@@ -52,8 +62,8 @@ COMPAT_INC="$ROOT/tests/compat"
|
||||
-o "$BUILD/zip_extract.o" "$ROOT/src/zip_extract.c"
|
||||
|
||||
"$CC" -c -O2 -Wall -Wextra -Wno-unused-parameter \
|
||||
-I"$ROOT/third_party/minizip-ng/include" -I"$ROOT/src" -I"$COMPAT_INC" \
|
||||
"${RAR_CFLAGS[@]}" -include "$ROOT/tests/posix_compat.h" \
|
||||
-I"$ROOT/third_party/minizip-ng/include" -I"$ROOT/third_party/unrar7" -I"$ROOT/src" -I"$COMPAT_INC" \
|
||||
-include "$ROOT/tests/posix_compat.h" \
|
||||
-o "$BUILD/rar_extract.o" "$ROOT/src/rar_extract.c"
|
||||
|
||||
"$CC" -c -O2 -Wall -Wextra -Wno-unused-parameter -I"$ROOT/src" \
|
||||
@@ -61,13 +71,15 @@ COMPAT_INC="$ROOT/tests/compat"
|
||||
-o "$BUILD/test_zip_extract.o" "$ROOT/tests/test_zip_extract.c"
|
||||
|
||||
"$CC" -c -O2 -Wall -Wextra -Wno-unused-parameter -I"$ROOT/src" \
|
||||
-I"$COMPAT_INC" "${RAR_CFLAGS[@]}" -include "$ROOT/tests/posix_compat.h" \
|
||||
-I"$COMPAT_INC" -include "$ROOT/tests/posix_compat.h" \
|
||||
-o "$BUILD/test_rar_extract.o" "$ROOT/tests/test_rar_extract.c"
|
||||
|
||||
objs=()
|
||||
rar_objs=()
|
||||
for obj in "$BUILD"/*.o; do
|
||||
case "$obj" in
|
||||
*/zip_extract.o|*/rar_extract.o|*/test_zip_extract.o|*/test_rar_extract.o) continue ;;
|
||||
*/unrar7_*.o) rar_objs+=("$obj"); continue ;;
|
||||
esac
|
||||
objs+=("$obj")
|
||||
done
|
||||
@@ -75,9 +87,17 @@ done
|
||||
"$CC" -O2 -o "$BUILD/test-zip-extract" \
|
||||
"$BUILD/zip_extract.o" "$BUILD/test_zip_extract.o" "${objs[@]}"
|
||||
|
||||
"$CC" -O2 -o "$BUILD/test-rar-extract" \
|
||||
# The RAR test links the unrar7 objects, so it needs the C++ driver.
|
||||
# Windows unrar system.cpp references SetSuspendState (PowrProf).
|
||||
RAR_LIBS=()
|
||||
[ "$HOST_KIND" = windows ] && RAR_LIBS=(-lpowrprof)
|
||||
"$CXX" -O2 -o "$BUILD/test-rar-extract" \
|
||||
"$BUILD/rar_extract.o" "$BUILD/test_rar_extract.o" \
|
||||
"$BUILD/zip_extract.o" "${objs[@]}"
|
||||
"$BUILD/zip_extract.o" "${objs[@]}" "${rar_objs[@]}" "${RAR_LIBS[@]}"
|
||||
|
||||
"$BUILD/test-zip-extract" "$ROOT/tests/fixtures" "$BUILD/work-zip"
|
||||
"$BUILD/test-rar-extract" "$ROOT/tests/fixtures" "$BUILD/work-rar"
|
||||
# Real RAR fixtures (v6 / multi-volume / encrypted) live in fixtures-real/,
|
||||
# generated by tests/make-rar-fixtures.bat (WinRAR required); fixtures/
|
||||
# itself is wiped by make_fixtures.py on every run.
|
||||
"$BUILD/test-rar-extract" "$ROOT/tests/fixtures" "$BUILD/work-rar" \
|
||||
"$ROOT/tests/fixtures-real"
|
||||
+127
-1
@@ -28,6 +28,7 @@
|
||||
#include "posix_compat.h"
|
||||
|
||||
static const char *g_fixtures;
|
||||
static const char *g_fixtures_real;
|
||||
static char g_work[4096];
|
||||
static int g_failures;
|
||||
static int g_checks;
|
||||
@@ -37,6 +38,11 @@ fixture_path(char *out, size_t size, const char *name) {
|
||||
snprintf(out, size, "%s/%s", g_fixtures, name);
|
||||
}
|
||||
|
||||
static void
|
||||
fixture_real_path(char *out, size_t size, const char *name) {
|
||||
snprintf(out, size, "%s/%s", g_fixtures_real, name);
|
||||
}
|
||||
|
||||
static void
|
||||
work_path(char *out, size_t size, const char *name) {
|
||||
snprintf(out, size, "%s/%s", g_work, name);
|
||||
@@ -298,13 +304,132 @@ test_limits_handoff(void) {
|
||||
"default() matches ZIPX_LIMITS_DEFAULT");
|
||||
}
|
||||
|
||||
/* Real-archive happy paths (v1.9, unrar 7.20.1 engine).
|
||||
The fixtures are produced by tests/make-rar-fixtures.bat on a machine with
|
||||
WinRAR; when they are missing (plain CI checkout) these checks SKIP. */
|
||||
static void
|
||||
test_real_archives(void) {
|
||||
char dst[4096];
|
||||
char checkp[4096];
|
||||
|
||||
printf("test_real_archives\n");
|
||||
|
||||
/* RAR5 (WinRAR 6/7 "v6" compression) single volume. */
|
||||
work_path(dst, sizeof(dst), "rdst_v6");
|
||||
remove_dir(dst);
|
||||
{
|
||||
zipx_result_t res;
|
||||
zipx_status_t st;
|
||||
char src[4096];
|
||||
fixture_real_path(src, sizeof(src), "basic-v6.rar");
|
||||
if(exists(src)) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
st = rar_extract(src, dst, ZIPX_CONFLICT_FAIL,
|
||||
zipx_default_limits(), NULL, NULL, NULL, &res);
|
||||
check(st == ZIPX_OK, "basic-v6.rar extracts (v6 RAR5)");
|
||||
if(st == ZIPX_OK) {
|
||||
snprintf(checkp, sizeof(checkp), "%s/root.txt", dst);
|
||||
check(exists(checkp), " root.txt present");
|
||||
snprintf(checkp, sizeof(checkp), "%s/dir/nested.txt", dst);
|
||||
check(exists(checkp), " dir/nested.txt present");
|
||||
check(res.entries_done >= 3, " entries_done >= 3");
|
||||
} else {
|
||||
printf(" message=%s\n", res.message);
|
||||
}
|
||||
} else {
|
||||
printf(" SKIP basic-v6.rar (missing — run tests/make-rar-fixtures.bat)\n");
|
||||
}
|
||||
}
|
||||
remove_dir(dst);
|
||||
|
||||
/* RAR4 legacy single volume. */
|
||||
work_path(dst, sizeof(dst), "rdst_r4");
|
||||
remove_dir(dst);
|
||||
{
|
||||
zipx_result_t res;
|
||||
zipx_status_t st;
|
||||
char src[4096];
|
||||
fixture_real_path(src, sizeof(src), "basic-rar4.rar");
|
||||
if(exists(src)) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
st = rar_extract(src, dst, ZIPX_CONFLICT_FAIL,
|
||||
zipx_default_limits(), NULL, NULL, NULL, &res);
|
||||
check(st == ZIPX_OK, "basic-rar4.rar extracts (RAR4)");
|
||||
if(st == ZIPX_OK) {
|
||||
snprintf(checkp, sizeof(checkp), "%s/root.txt", dst);
|
||||
check(exists(checkp), " root.txt present");
|
||||
} else {
|
||||
printf(" message=%s\n", res.message);
|
||||
}
|
||||
} else {
|
||||
printf(" SKIP basic-rar4.rar (missing)\n");
|
||||
}
|
||||
}
|
||||
remove_dir(dst);
|
||||
|
||||
/* Multi-volume: opening vol.part1.rar must auto-merge part2 from the same
|
||||
directory (unrar drives the volume chain). */
|
||||
work_path(dst, sizeof(dst), "rdst_vol");
|
||||
remove_dir(dst);
|
||||
{
|
||||
zipx_result_t res;
|
||||
zipx_status_t st;
|
||||
char src[4096];
|
||||
fixture_real_path(src, sizeof(src), "vol.part1.rar");
|
||||
if(exists(src)) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
st = rar_extract(src, dst, ZIPX_CONFLICT_FAIL,
|
||||
zipx_default_limits(), NULL, NULL, NULL, &res);
|
||||
check(st == ZIPX_OK, "vol.part1.rar auto-merges volumes");
|
||||
if(st == ZIPX_OK) {
|
||||
snprintf(checkp, sizeof(checkp), "%s/root.txt", dst);
|
||||
check(exists(checkp), " root.txt present across volumes");
|
||||
snprintf(checkp, sizeof(checkp), "%s/big.bin", dst);
|
||||
check(exists(checkp), " big.bin (split file) present and whole");
|
||||
check(res.entries_done >= 2, " entries_done >= 2");
|
||||
} else {
|
||||
printf(" message=%s\n", res.message);
|
||||
}
|
||||
} else {
|
||||
printf(" SKIP vol.part1.rar (missing)\n");
|
||||
}
|
||||
}
|
||||
remove_dir(dst);
|
||||
|
||||
/* Encrypted: engine can decrypt but the password channel is not wired yet,
|
||||
so encrypted entries must be rejected up front with UNSUPPORTED. */
|
||||
work_path(dst, sizeof(dst), "rdst_enc");
|
||||
remove_dir(dst);
|
||||
{
|
||||
zipx_result_t res;
|
||||
zipx_status_t st;
|
||||
char src[4096];
|
||||
fixture_real_path(src, sizeof(src), "enc-v6.rar");
|
||||
if(exists(src)) {
|
||||
memset(&res, 0, sizeof(res));
|
||||
st = rar_extract(src, dst, ZIPX_CONFLICT_FAIL,
|
||||
zipx_default_limits(), NULL, NULL, NULL, &res);
|
||||
check(st == ZIPX_ERR_UNSUPPORTED, "enc-v6.rar rejected (no password channel)");
|
||||
check(!exists(dst), " no files written for encrypted archive");
|
||||
if(st == ZIPX_OK) {
|
||||
remove_dir(dst);
|
||||
}
|
||||
} else {
|
||||
printf(" SKIP enc-v6.rar (missing)\n");
|
||||
}
|
||||
}
|
||||
remove_dir(dst);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
if(argc < 3) {
|
||||
fprintf(stderr, "usage: %s <fixtures-dir> <work-dir>\n", argv[0]);
|
||||
fprintf(stderr, "usage: %s <fixtures-dir> <work-dir> [real-fixtures-dir]\n",
|
||||
argv[0]);
|
||||
return 2;
|
||||
}
|
||||
g_fixtures = argv[1];
|
||||
g_fixtures_real = (argc >= 4) ? argv[3] : argv[1];
|
||||
snprintf(g_work, sizeof(g_work), "%s", argv[2]);
|
||||
mkdir(g_work, 0777);
|
||||
|
||||
@@ -341,6 +466,7 @@ main(int argc, char **argv) {
|
||||
test_engine_dispatch();
|
||||
test_format_translation();
|
||||
test_limits_handoff();
|
||||
test_real_archives();
|
||||
|
||||
printf("\nrar_extract: %d checks, %d failures\n", g_checks, g_failures);
|
||||
return g_failures == 0 ? 0 : 1;
|
||||
|
||||
+642
-639
File diff suppressed because it is too large.
Load diff
Vendored
-339
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
dmc_unrar README
|
||||
================
|
||||
|
||||
dmc_unrar is a dependency-free, single-file FLOSS library for unpacking
|
||||
and decompressing [RAR](https://en.wikipedia.org/wiki/RAR_(file_format))
|
||||
archives. dmc_unrar is licensed under the terms of the [GNU General
|
||||
Public License version 2](https://www.gnu.org/licenses/gpl-2.0.html) (or
|
||||
later).
|
||||
|
||||
Please see dmc_unrar.c for details.
|
||||
Vendored
-77
@@ -1,77 +0,0 @@
|
||||
# Vendored: dmc_unrar 1.7.0
|
||||
|
||||
## Source
|
||||
|
||||
- Repository: https://github.com/DrMcCoy/dmc_unrar
|
||||
- Upstream commit: master @ 2026-09-05 (DRMcCoy/dmc_unrar master)
|
||||
- Author: Sven Hesse (DrMcCoy) <drmccoy@drmccoy.de>
|
||||
- License: GPL-2.0-or-later (see COPYING in this directory)
|
||||
|
||||
We vendor only the implementation source file `dmc_unrar.c` (11598 LOC, ~365 KiB).
|
||||
The repository does not ship a separate header; the entire API is exposed
|
||||
inline in the `.c` file (see top-of-file docstrings + `example.c`).
|
||||
|
||||
## What is supported
|
||||
|
||||
- RAR 1.5 / 2.0 / 2.6 / 2.9 / 3.0 / 3.6 / 4.0 / 5.0 archive formats
|
||||
- Solid blocks, dictionary sizes up to 4 MiB (RAR4) / 32 MiB (RAR5)
|
||||
- PPMd decompression (used by RAR 3.0+)
|
||||
- Archive comments (ASCII / UTF-16LE)
|
||||
- Encrypted archive/file *detection* (returns an explicit error)
|
||||
|
||||
## What is NOT supported (by upstream design)
|
||||
|
||||
- **Encrypted RAR files** — `DMC_UNRAR_(ARCHIVE|FILE)_UNSUPPORTED_ENCRYPTED`.
|
||||
We surface this as `ZIPX_ERR_UNSUPPORTED` in the wrapper. Upstream
|
||||
intentionally omits decryption to avoid patent complications.
|
||||
- **Multi-volume (split) RAR archives** — `DMC_UNRAR_ARCHIVE_UNSUPPORTED_VOLUMES`.
|
||||
We surface this as `ZIPX_ERR_UNSUPPORTED`. dmc_unrar does not chain
|
||||
`.partNN.rar` siblings; users must join / unrar on a PC first.
|
||||
- Symbolic links, FIFOs, sockets, devices — `DMC_UNRAR_FILE_UNSUPPORTED_LINK`.
|
||||
|
||||
## Why dmc_unrar and not the upstream rarlab UnRAR
|
||||
|
||||
The rarlab UnRAR source tree (https://www.rarlab.com/rar/unrarsrc-*.tar.gz,
|
||||
mirrored at https://github.com/opello/unrar) supports encrypted and
|
||||
multi-volume archives, but it is a 150-file C++17 codebase with its own
|
||||
filesystem abstraction, Windows registry probe, and threading pool that
|
||||
requires `-DRAR_SMP`. Integrating it cleanly into the PS5 toolchain would
|
||||
need:
|
||||
|
||||
1. A separate CXX linker step in the Makefile.
|
||||
2. Verification that `prospero-clang` (-std=c++17, freestanding) builds it
|
||||
without libc++ dependencies beyond what `prospero-pkg-config` exposes.
|
||||
3. A new exit-error contract for the `--strip` toolchain.
|
||||
4. ~10× the audit surface (every `#include <windows.h>` etc.).
|
||||
|
||||
We decided the cost > benefit for v1.8: single-volume RAR is the dominant
|
||||
PS5 Web File Manager use case (uploading an unzipped payload, dumping a
|
||||
single .pkg as `.rar` for transfer). Users needing multi-volume /
|
||||
encrypted RAR are explicitly informed via the web UI error message that
|
||||
they should extract on a PC first.
|
||||
|
||||
## Upgrading to a fuller library (v1.9 plan)
|
||||
|
||||
When (if) demand justifies it, the recommended upgrade path is:
|
||||
|
||||
1. Vendor `https://github.com/opello/unrar` (a faithful mirror of rarlab's
|
||||
UnRAR 7.x) into `third_party/unrar/` (replacing dmc_unrar.c).
|
||||
2. Add a CXX compilation rule in `Makefile`:
|
||||
```
|
||||
THIRD_PARTY_CPP_SRCS := $(wildcard third_party/unrar/*.cpp)
|
||||
PS5_TP_CPP_OBJS := $(patsubst %.cpp,ps5-obj/%.o,$(THIRD_PARTY_CPP_SRCS))
|
||||
$(BIN): ... $(PS5_TP_CPP_OBJS)
|
||||
$(CXX) -o $@ ... $(PS5_TP_CPP_OBJS) ...
|
||||
```
|
||||
3. Build dmc_unrar-style driver code against the DLL API
|
||||
(`-DRARDLL`, link via `dll.cpp`). The RAR API entry points
|
||||
`RAROpenArchiveEx`, `RARReadHeaderEx`, `RARProcessFileW` accept the
|
||||
password via `RARSetPassword`.
|
||||
4. Rewrite `src/rar_extract.c` to call the new engine while preserving
|
||||
the `rar_extract()` signature and the `zipx_status_t` error mapping.
|
||||
5. Tests in `tests/test_rar_extract.c` should not need to change — only
|
||||
the engine behind `rar_extract()`.
|
||||
|
||||
## Modifications to dmc_unrar.c
|
||||
|
||||
None. We pin the upstream file verbatim to keep the upgrade path trivial.
|
||||
Vendored
-11598
File diff suppressed because it is too large.
Load diff
Vendored
-139
@@ -1,139 +0,0 @@
|
||||
/* Facade header for dmc_unrar 1.7.0 (vendored at third_party/unrar/dmc_unrar.c).
|
||||
*
|
||||
* This file declares only the dmc_unrar entry points that src/rar_extract.c
|
||||
* actually uses. It exists so that rar_extract.c does NOT have to
|
||||
* `#include "dmc_unrar.c"` — that would otherwise pull the entire library
|
||||
* into the same translation unit, where the host test build's
|
||||
* tests/posix_compat.h renames `open` / `close` to `wfm_open` / `wfm_close`
|
||||
* and breaks dmc_unrar's `dmc_unrar_io_handler` struct member access.
|
||||
*
|
||||
* On the PS5 the build (Makefile) compiles dmc_unrar.c separately into
|
||||
* `ps5-obj/.../dmc_unrar.o` and links it with the rest. On the host test
|
||||
* runner, `tests/run-tests.sh` does the same with `dmc_unrar.o` (built
|
||||
* without the POSIX compat shim) and `rar_extract.o` (built with it). */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* DMC_UNRAR_SIZE_T and DMC_UNRAR_OFFSET_T are normally typedef'd by
|
||||
dmc_unrar.c. Mirror them here so consumers do not need to include it. */
|
||||
#ifndef DMC_UNRAR_SIZE_T
|
||||
typedef uint64_t dmc_unrar_size_t;
|
||||
typedef int64_t dmc_unrar_offset_t;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
DMC_UNRAR_OK = 0,
|
||||
|
||||
DMC_UNRAR_NO_ALLOC,
|
||||
DMC_UNRAR_ALLOC_FAIL,
|
||||
|
||||
DMC_UNRAR_OPEN_FAIL,
|
||||
DMC_UNRAR_READ_FAIL,
|
||||
DMC_UNRAR_WRITE_FAIL,
|
||||
DMC_UNRAR_SEEK_FAIL,
|
||||
|
||||
DMC_UNRAR_INVALID_DATA,
|
||||
|
||||
DMC_UNRAR_ARCHIVE_EMPTY,
|
||||
|
||||
DMC_UNRAR_ARCHIVE_IS_NULL,
|
||||
DMC_UNRAR_ARCHIVE_NOT_CLEARED,
|
||||
DMC_UNRAR_ARCHIVE_MISSING_FIELDS,
|
||||
|
||||
DMC_UNRAR_ARCHIVE_NOT_RAR,
|
||||
DMC_UNRAR_ARCHIVE_UNSUPPORTED_ANCIENT,
|
||||
|
||||
DMC_UNRAR_ARCHIVE_UNSUPPORTED_VOLUMES,
|
||||
DMC_UNRAR_ARCHIVE_UNSUPPORTED_ENCRYPTED,
|
||||
|
||||
DMC_UNRAR_FILE_IS_INVALID,
|
||||
DMC_UNRAR_FILE_IS_DIRECTORY,
|
||||
|
||||
DMC_UNRAR_FILE_SOLID_BROKEN,
|
||||
DMC_UNRAR_FILE_CRC32_FAIL,
|
||||
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_VERSION,
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_METHOD,
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_ENCRYPTED,
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_SPLIT,
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_LINK,
|
||||
DMC_UNRAR_FILE_UNSUPPORTED_LARGE
|
||||
/* Values after DMC_UNRAR_FILE_UNSUPPORTED_LARGE (HUFF_*, PPMD_*, FILTERS_*,
|
||||
*_DISABLED_FEATURE_*, RAR15/20/30/50 specific codes) are intentionally
|
||||
omitted — rar_extract.c only translates the codes it knows about and
|
||||
groups the rest into ZIPX_ERR_FORMAT via the default case. */
|
||||
} dmc_unrar_return;
|
||||
|
||||
typedef enum {
|
||||
DMC_UNRAR_HOSTOS_DOS = 0,
|
||||
DMC_UNRAR_HOSTOS_OS2 = 1,
|
||||
DMC_UNRAR_HOSTOS_WIN32 = 2,
|
||||
DMC_UNRAR_HOSTOS_UNIX = 3,
|
||||
DMC_UNRAR_HOSTOS_MACOS = 4,
|
||||
DMC_UNRAR_HOSTOS_BEOS = 5
|
||||
} dmc_unrar_host_os;
|
||||
|
||||
typedef struct dmc_unrar_file_tag {
|
||||
uint64_t compressed_size;
|
||||
uint64_t uncompressed_size;
|
||||
dmc_unrar_host_os host_os;
|
||||
bool has_crc;
|
||||
uint32_t crc;
|
||||
uint64_t unix_time;
|
||||
uint64_t attrs;
|
||||
} dmc_unrar_file;
|
||||
|
||||
/* dmc_unrar_archive is an opaque struct in the vendored library. We expose
|
||||
it here so callers can stack-allocate one and pass &rar to the API. The
|
||||
field list is *not* required — the dmc_unrar API treats the struct as a
|
||||
token, but we need a complete type so callers can declare it as a local
|
||||
variable. The layout is irrelevant because the dmc_unrar API only uses
|
||||
pointers and the real definition lives inside dmc_unrar.c. */
|
||||
typedef struct dmc_unrar_archive_tag {
|
||||
void *alloc;
|
||||
void *io;
|
||||
void *internal_state;
|
||||
} dmc_unrar_archive;
|
||||
|
||||
const char *dmc_unrar_strerror(dmc_unrar_return code);
|
||||
|
||||
bool dmc_unrar_is_rar_path(const char *path);
|
||||
|
||||
dmc_unrar_return dmc_unrar_archive_init(dmc_unrar_archive *archive);
|
||||
void dmc_unrar_archive_close(dmc_unrar_archive *archive);
|
||||
dmc_unrar_return dmc_unrar_archive_open_path(dmc_unrar_archive *archive,
|
||||
const char *path);
|
||||
|
||||
dmc_unrar_size_t dmc_unrar_get_file_count(dmc_unrar_archive *archive);
|
||||
const dmc_unrar_file *dmc_unrar_get_file_stat(dmc_unrar_archive *archive,
|
||||
dmc_unrar_size_t index);
|
||||
|
||||
dmc_unrar_size_t dmc_unrar_get_filename(dmc_unrar_archive *archive,
|
||||
dmc_unrar_size_t index,
|
||||
char *filename,
|
||||
dmc_unrar_size_t filename_size);
|
||||
bool dmc_unrar_file_is_directory(dmc_unrar_archive *archive,
|
||||
dmc_unrar_size_t index);
|
||||
dmc_unrar_return dmc_unrar_file_is_supported(dmc_unrar_archive *archive,
|
||||
dmc_unrar_size_t index);
|
||||
|
||||
bool dmc_unrar_unicode_make_valid_utf8(char *str);
|
||||
|
||||
dmc_unrar_return dmc_unrar_extract_file_to_path(dmc_unrar_archive *archive,
|
||||
dmc_unrar_size_t index,
|
||||
const char *path,
|
||||
dmc_unrar_size_t *uncompressed_size,
|
||||
bool validate_crc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Vendored
-239
@@ -1,239 +0,0 @@
|
||||
/* dmc_unrar - A dependency-free, single-file FLOSS unrar library
|
||||
*
|
||||
* Copyright (c) 2017 by Sven Hesse (DrMcCoy) <drmccoy@drmccoy.de>
|
||||
*
|
||||
* dmc_unrar is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* dmc_unrar is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* For the full text of the GNU General Public License version 2,
|
||||
* see <https://www.gnu.org/licenses/gpl-2.0.html>
|
||||
*/
|
||||
|
||||
/* This is a small example program to show how to use dmc_unrar. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dmc_unrar.c"
|
||||
|
||||
int display_help(const char *name);
|
||||
int display_error(const char *where, const char *str);
|
||||
|
||||
char get_command(const char *param);
|
||||
|
||||
const char *get_archive_comment(dmc_unrar_archive *archive);
|
||||
const char *get_file_comment(dmc_unrar_archive *archive, dmc_unrar_size_t i);
|
||||
const char *get_filename(dmc_unrar_archive *archive, dmc_unrar_size_t i);
|
||||
|
||||
const char *get_filename_no_directory(const char *filename);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
dmc_unrar_size_t i;
|
||||
char cmd;
|
||||
|
||||
const char *comment = NULL;
|
||||
dmc_unrar_archive archive;
|
||||
dmc_unrar_return return_code;
|
||||
|
||||
if (argc < 3)
|
||||
return display_help(argv[0]);
|
||||
|
||||
cmd = get_command(argv[1]);
|
||||
if (cmd == 0)
|
||||
return display_help(argv[0]);
|
||||
|
||||
if (!dmc_unrar_is_rar_path(argv[2]))
|
||||
return display_help(argv[0]);
|
||||
|
||||
return_code = dmc_unrar_archive_init(&archive);
|
||||
if (return_code != DMC_UNRAR_OK)
|
||||
return display_error("Clear", dmc_unrar_strerror(return_code));
|
||||
|
||||
return_code = dmc_unrar_archive_open_path(&archive, argv[2]);
|
||||
if (return_code != DMC_UNRAR_OK)
|
||||
return display_error("Open", dmc_unrar_strerror(return_code));
|
||||
|
||||
comment = get_archive_comment(&archive);
|
||||
if (comment) {
|
||||
printf(".--- Archive comment:\n");
|
||||
printf("%s\n", comment);
|
||||
printf("'---\n\n");
|
||||
free((char *)comment);
|
||||
}
|
||||
|
||||
for (i = 0; i < dmc_unrar_get_file_count(&archive); i++) {
|
||||
const char *name = get_filename(&archive, i), *file_comment = NULL;
|
||||
const dmc_unrar_file *file = dmc_unrar_get_file_stat(&archive, i);
|
||||
|
||||
printf("%u/%u: \"%s\" - %llu bytes\n",
|
||||
(unsigned int)(i+1), (unsigned int)dmc_unrar_get_file_count(&archive),
|
||||
name ? name : "", file ? (unsigned long long)file->uncompressed_size : (unsigned long long)0);
|
||||
|
||||
file_comment = get_file_comment(&archive, i);
|
||||
if (file_comment) {
|
||||
printf(".--- File comment:\n");
|
||||
printf("%s\n", file_comment);
|
||||
printf("'---\n\n");
|
||||
free((char *)file_comment);
|
||||
}
|
||||
|
||||
if (cmd == 'e') {
|
||||
const char *filename = get_filename_no_directory(name);
|
||||
|
||||
if (filename && !dmc_unrar_file_is_directory(&archive, i)) {
|
||||
dmc_unrar_return supported = dmc_unrar_file_is_supported(&archive, i);
|
||||
if (supported == DMC_UNRAR_OK) {
|
||||
dmc_unrar_return extracted = dmc_unrar_extract_file_to_path(&archive, i, filename, NULL, true);
|
||||
if (extracted != DMC_UNRAR_OK)
|
||||
fprintf(stderr, "Error: %s\n", dmc_unrar_strerror(extracted));
|
||||
|
||||
} else
|
||||
fprintf(stderr, "Not supported: %s\n", dmc_unrar_strerror(supported));
|
||||
}
|
||||
}
|
||||
|
||||
free((char *)name);
|
||||
}
|
||||
|
||||
dmc_unrar_archive_close(&archive);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int display_help(const char *name) {
|
||||
fprintf(stderr, "dmc_unrar - A dependency-free, single-file FLOSS unrar library\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Usage: %s <command> <rarfile>\n", name);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Commands:\n");
|
||||
fprintf(stderr, " l List RAR archive contents\n");
|
||||
fprintf(stderr, " e Extract RAR archive to current directory\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int display_error(const char *where, const char *str) {
|
||||
fprintf(stderr, "%s failed: %s\n", where, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char get_command(const char *param) {
|
||||
char cmd;
|
||||
|
||||
if (!param || (param[0] == '\0') || (param[1] != '\0'))
|
||||
return 0;
|
||||
|
||||
cmd = param[0];
|
||||
if ((cmd != 'l') && (cmd != 'e'))
|
||||
return 0;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static const char *convert_comment(uint8_t *data, dmc_unrar_size_t size) {
|
||||
const dmc_unrar_unicode_encoding encoding = dmc_unrar_unicode_detect_encoding(data, size);
|
||||
if (encoding == DMC_UNRAR_UNICODE_ENCODING_UTF8) {
|
||||
data[size] = '\0';
|
||||
return (const char *)data;
|
||||
}
|
||||
|
||||
if (encoding == DMC_UNRAR_UNICODE_ENCODING_UTF16LE) {
|
||||
char *utf8_data = NULL;
|
||||
|
||||
dmc_unrar_size_t utf8_size = dmc_unrar_unicode_convert_utf16le_to_utf8(data, size, NULL, 0);
|
||||
if (utf8_size) {
|
||||
utf8_data = (char *)malloc(utf8_size);
|
||||
if (utf8_data) {
|
||||
utf8_size = dmc_unrar_unicode_convert_utf16le_to_utf8(data, size, utf8_data, utf8_size);
|
||||
if (utf8_size) {
|
||||
free(data);
|
||||
return utf8_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
free(utf8_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *get_archive_comment(dmc_unrar_archive *archive) {
|
||||
uint8_t *data = NULL;
|
||||
dmc_unrar_size_t size = dmc_unrar_get_archive_comment(archive, NULL, 0);
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
data = (uint8_t *)malloc(size + 1);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
size = dmc_unrar_get_archive_comment(archive, data, size);
|
||||
return convert_comment(data, size);
|
||||
}
|
||||
|
||||
const char *get_file_comment(dmc_unrar_archive *archive, dmc_unrar_size_t i) {
|
||||
uint8_t *data = NULL;
|
||||
dmc_unrar_size_t size = dmc_unrar_get_file_comment(archive, i, NULL, 0);
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
data = (uint8_t *)malloc(size + 1);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
size = dmc_unrar_get_file_comment(archive, i, data, size);
|
||||
return convert_comment(data, size);
|
||||
}
|
||||
|
||||
const char *get_filename(dmc_unrar_archive *archive, dmc_unrar_size_t i) {
|
||||
char *filename = 0;
|
||||
dmc_unrar_size_t size = dmc_unrar_get_filename(archive, i, NULL, 0);
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
filename = (char *)malloc(size);
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
size = dmc_unrar_get_filename(archive, i, filename, size);
|
||||
if (!size) {
|
||||
free(filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dmc_unrar_unicode_make_valid_utf8(filename);
|
||||
if (filename[0] == '\0') {
|
||||
free(filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
const char *get_filename_no_directory(const char *filename) {
|
||||
char *p = NULL;
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
p = (char *) strrchr(filename, '/');
|
||||
if (!p)
|
||||
return filename;
|
||||
|
||||
if (p[1] == '\0')
|
||||
return NULL;
|
||||
|
||||
return p + 1;
|
||||
}
|
||||
Vendored
+281
@@ -0,0 +1,281 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{95CC809B-03FC-4EDB-BB20-FD07A698C05F}</ProjectGuid>
|
||||
<RootNamespace>UnRAR</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>build\unrar32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>build\unrar64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>build\unrar32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>build\unrar64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="archive.cpp" />
|
||||
<ClCompile Include="arcread.cpp" />
|
||||
<ClCompile Include="blake2s.cpp" />
|
||||
<ClCompile Include="cmddata.cpp" />
|
||||
<ClCompile Include="consio.cpp" />
|
||||
<ClCompile Include="crc.cpp" />
|
||||
<ClCompile Include="crypt.cpp" />
|
||||
<ClCompile Include="encname.cpp" />
|
||||
<ClCompile Include="errhnd.cpp" />
|
||||
<ClCompile Include="extinfo.cpp" />
|
||||
<ClCompile Include="extract.cpp" />
|
||||
<ClCompile Include="filcreat.cpp" />
|
||||
<ClCompile Include="file.cpp" />
|
||||
<ClCompile Include="filefn.cpp" />
|
||||
<ClCompile Include="filestr.cpp" />
|
||||
<ClCompile Include="find.cpp" />
|
||||
<ClCompile Include="getbits.cpp" />
|
||||
<ClCompile Include="global.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hash.cpp" />
|
||||
<ClCompile Include="headers.cpp" />
|
||||
<ClCompile Include="isnt.cpp" />
|
||||
<ClCompile Include="largepage.cpp" />
|
||||
<ClCompile Include="list.cpp" />
|
||||
<ClCompile Include="match.cpp" />
|
||||
<ClCompile Include="motw.cpp" />
|
||||
<ClCompile Include="options.cpp" />
|
||||
<ClCompile Include="pathfn.cpp" />
|
||||
<ClCompile Include="qopen.cpp" />
|
||||
<ClCompile Include="rar.cpp" />
|
||||
<ClCompile Include="rarpch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rarvm.cpp" />
|
||||
<ClCompile Include="rawread.cpp" />
|
||||
<ClCompile Include="rdwrfn.cpp" />
|
||||
<ClCompile Include="recvol.cpp" />
|
||||
<ClCompile Include="resource.cpp" />
|
||||
<ClCompile Include="rijndael.cpp" />
|
||||
<ClCompile Include="rs.cpp" />
|
||||
<ClCompile Include="rs16.cpp" />
|
||||
<ClCompile Include="scantree.cpp" />
|
||||
<ClCompile Include="secpassword.cpp" />
|
||||
<ClCompile Include="sha1.cpp" />
|
||||
<ClCompile Include="sha256.cpp" />
|
||||
<ClCompile Include="smallfn.cpp" />
|
||||
<ClCompile Include="strfn.cpp" />
|
||||
<ClCompile Include="strlist.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="threadpool.cpp" />
|
||||
<ClCompile Include="timefn.cpp" />
|
||||
<ClCompile Include="ui.cpp" />
|
||||
<ClCompile Include="unicode.cpp" />
|
||||
<ClCompile Include="unpack.cpp" />
|
||||
<ClCompile Include="volume.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+422
@@ -0,0 +1,422 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="release_nocrypt|Win32">
|
||||
<Configuration>release_nocrypt</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="release_nocrypt|x64">
|
||||
<Configuration>release_nocrypt</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>UnRAR</ProjectName>
|
||||
<ProjectGuid>{E815C46C-36C4-499F-BBC2-E772C6B17971}</ProjectGuid>
|
||||
<RootNamespace>UnRAR</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140_xp</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
<OutDir>build\unrardll32\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
<OutDir>build\unrardll64\$(Configuration)\</OutDir>
|
||||
<IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>false</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StructMemberAlignment>Default</StructMemberAlignment>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CallingConvention>StdCall</CallingConvention>
|
||||
<DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)unrar.dll</OutputFile>
|
||||
<ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<LinkTimeCodeGeneration />
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="archive.cpp" />
|
||||
<ClCompile Include="arcread.cpp" />
|
||||
<ClCompile Include="blake2s.cpp" />
|
||||
<ClCompile Include="cmddata.cpp" />
|
||||
<ClCompile Include="consio.cpp" />
|
||||
<ClCompile Include="crc.cpp" />
|
||||
<ClCompile Include="crypt.cpp" />
|
||||
<ClCompile Include="dll.cpp" />
|
||||
<ClCompile Include="encname.cpp" />
|
||||
<ClCompile Include="errhnd.cpp" />
|
||||
<ClCompile Include="extinfo.cpp" />
|
||||
<ClCompile Include="extract.cpp" />
|
||||
<ClCompile Include="filcreat.cpp" />
|
||||
<ClCompile Include="file.cpp" />
|
||||
<ClCompile Include="filefn.cpp" />
|
||||
<ClCompile Include="filestr.cpp" />
|
||||
<ClCompile Include="find.cpp" />
|
||||
<ClCompile Include="getbits.cpp" />
|
||||
<ClCompile Include="global.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hash.cpp" />
|
||||
<ClCompile Include="headers.cpp" />
|
||||
<ClCompile Include="isnt.cpp" />
|
||||
<ClCompile Include="largepage.cpp" />
|
||||
<ClCompile Include="match.cpp" />
|
||||
<ClCompile Include="motw.cpp" />
|
||||
<ClCompile Include="options.cpp" />
|
||||
<ClCompile Include="pathfn.cpp" />
|
||||
<ClCompile Include="qopen.cpp" />
|
||||
<ClCompile Include="rar.cpp" />
|
||||
<ClCompile Include="rarpch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rarvm.cpp" />
|
||||
<ClCompile Include="rawread.cpp" />
|
||||
<ClCompile Include="rdwrfn.cpp" />
|
||||
<ClCompile Include="rijndael.cpp" />
|
||||
<ClCompile Include="rs.cpp" />
|
||||
<ClCompile Include="rs16.cpp" />
|
||||
<ClCompile Include="scantree.cpp" />
|
||||
<ClCompile Include="secpassword.cpp" />
|
||||
<ClCompile Include="sha1.cpp" />
|
||||
<ClCompile Include="sha256.cpp" />
|
||||
<ClCompile Include="smallfn.cpp" />
|
||||
<ClCompile Include="strfn.cpp" />
|
||||
<ClCompile Include="strlist.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="threadpool.cpp" />
|
||||
<ClCompile Include="timefn.cpp" />
|
||||
<ClCompile Include="ui.cpp" />
|
||||
<ClCompile Include="unicode.cpp" />
|
||||
<ClCompile Include="unpack.cpp" />
|
||||
<ClCompile Include="volume.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="rar.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="dll.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
# Vendored: unrar 7.20.1 (RARLab UnRAR source)
|
||||
|
||||
Replaces `dmc_unrar` (see git history under `third_party/unrar/`) as the RAR
|
||||
engine. This is the official Alexander Roshal UnRAR source, packaged on
|
||||
GitHub by opello for easier vendoring.
|
||||
|
||||
## Source
|
||||
- Upstream: https://github.com/opello/unrar — commit `97e1780310`
|
||||
("v7.20.1: Extracted from https://www.rarlab.com/rar/unrarsrc-...", 2025-10-30)
|
||||
- Version: 7.20.1 (see `version.hpp`: RARVER_MAJOR 7, RARVER_MINOR 20, BETA 1,
|
||||
built 2025-10-28)
|
||||
- Mirror of RARLab https://www.rarlab.com/rar_add.htm — this directory is a
|
||||
verbatim copy of the upstream tree (159 files), no modifications except the
|
||||
additions listed below.
|
||||
|
||||
## What this enables that dmc_unrar could not
|
||||
- **RAR5 compression version 6** (WinRAR 6.x / 7.x archives, "v6:" algorithm
|
||||
string). dmc_unrar 1.7.0 only dispatched `0x5000` (v5) in
|
||||
`dmc_unrar_file_unpack`, so any WinRAR 6+ archive hit
|
||||
`DMC_UNRAR_FILE_UNSUPPORTED_VERSION` and surfaced as "corrupt archive".
|
||||
- **Encrypted RAR** (headers and/or entries) — password via `RARSetPassword`.
|
||||
- **Multi-volume RAR** (`.partNN.rar` chains) — via open-mode + volume
|
||||
callback handling.
|
||||
- RAR4 (all sub-versions) continues to work.
|
||||
|
||||
## Build model
|
||||
Compile the upstream DLL source set as a **static library**, define
|
||||
`-DRARDLL` (suppresses `rar.cpp`'s `main()` via `#if !defined(RARDLL)`).
|
||||
|
||||
Source set (49 .cpp, matching `UnRARDll.vcxproj` ClCompile list):
|
||||
archive arcread blake2s cmddata consio crc crypt dll encname errhnd extinfo
|
||||
extract filcreat file filefn filestr find getbits global hash headers isnt
|
||||
largepage match motw options pathfn qopen rar rarpch rarvm rawread rdwrfn
|
||||
rijndael rs rs16 scantree secpassword sha1 sha256 smallfn strfn strlist
|
||||
system threadpool timefn ui unicode unpack volume
|
||||
|
||||
Compile flags:
|
||||
- `-std=c++17 -DRARDLL -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE`
|
||||
- PS5 (`prospero-clang++`): the toolchain defaults to `-stdlib=libc++`
|
||||
(FreeBSD-style sysroot; there is no GNU libstdc++). No explicit `-lc++`
|
||||
needed. `_UNIX` is defined by the platform headers.
|
||||
- Host tests (Windows/MinGW `g++`): libstdc++ default is fine.
|
||||
- Windows link needs `-lpowrprof` (SystemSuspend in `system.cpp`);
|
||||
`SetSuspendState` is referenced only on `_WIN_ALL`.
|
||||
|
||||
The upstream C API (`dll.hpp`) is C-compatible (extern "C" + plain C
|
||||
structs). C translation units include `unrar_c_api.h` (platform shim +
|
||||
re-export of the entry points used by `src/rar_extract.c`), never the unrar
|
||||
C++ headers.
|
||||
|
||||
## Additions in this directory (not upstream)
|
||||
- `unrar_c_api.h` — WFM facade shim (see file header).
|
||||
- `VENDORED.md` — this file.
|
||||
|
||||
## License
|
||||
UnRAR freeware license (`license.txt`): free to use in any software to
|
||||
handle RAR archives; may NOT be used to build a RAR-compatible *archiver* or
|
||||
re-create the RAR compression algorithm. Full text must stay with the
|
||||
source. This replaces the GPL-2.0 dmc_unrar notice in THIRD_PARTY_NOTICES.
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
ACKNOWLEDGMENTS
|
||||
|
||||
* We used "Screaming Fast Galois Field Arithmetic Using Intel
|
||||
SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
|
||||
and Ethan L. Miller to improve Reed-Solomon coding performance.
|
||||
Also we are grateful to Artem Drobanov and Bulat Ziganshin
|
||||
for samples and ideas allowed to make Reed-Solomon coding
|
||||
more efficient.
|
||||
|
||||
* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
|
||||
and Dmitry Subbotin carryless rangecoder public domain source code.
|
||||
You can find it in ftp.elf.stuba.sk/pub/pc/pack.
|
||||
|
||||
* RAR encryption includes parts of public domain code
|
||||
from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
|
||||
|
||||
* With exception of SFX modules, RAR uses CRC32 function based
|
||||
on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
|
||||
is available here:
|
||||
|
||||
https://sourceforge.net/projects/slicing-by-8/
|
||||
|
||||
Original Intel Slicing-by-8 code is licensed under BSD License
|
||||
available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
Copyright (c) 2004-2006 Intel Corporation.
|
||||
All Rights Reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
|
||||
designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
|
||||
and Christian Winnerlein.
|
||||
|
||||
* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
|
||||
to significantly improve RAR compression and speed.
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
static bool IsAnsiEscComment(const wchar *Data,size_t Size);
|
||||
|
||||
bool Archive::GetComment(std::wstring &CmtData)
|
||||
{
|
||||
if (!MainComment)
|
||||
return false;
|
||||
int64 SavePos=Tell();
|
||||
bool Success=DoGetComment(CmtData);
|
||||
Seek(SavePos,SEEK_SET);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::DoGetComment(std::wstring &CmtData)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
uint CmtLength;
|
||||
if (Format==RARFMT14)
|
||||
{
|
||||
Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
|
||||
CmtLength=GetByte();
|
||||
CmtLength+=(GetByte()<<8);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (MainHead.CommentInHeader)
|
||||
{
|
||||
// Old style (RAR 2.9) archive comment embedded into the main
|
||||
// archive header.
|
||||
Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
|
||||
if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Current (RAR 3.0+) version of archive comment.
|
||||
Seek(GetStartPos(),SEEK_SET);
|
||||
if (SearchSubBlock(SUBHEAD_TYPE_CMT)!=0)
|
||||
if (ReadCommentData(CmtData))
|
||||
return true;
|
||||
else
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
#ifndef SFX_MODULE
|
||||
// Old style (RAR 2.9) comment header embedded into the main
|
||||
// archive header.
|
||||
if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD)
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD;
|
||||
#endif
|
||||
}
|
||||
#ifndef SFX_MODULE
|
||||
if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30)
|
||||
{
|
||||
if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
|
||||
return false;
|
||||
ComprDataIO DataIO;
|
||||
DataIO.SetTestMode(true);
|
||||
uint UnpCmtLength;
|
||||
if (Format==RARFMT14)
|
||||
{
|
||||
#ifdef RAR_NOCRYPT
|
||||
return false;
|
||||
#else
|
||||
UnpCmtLength=GetByte();
|
||||
UnpCmtLength+=(GetByte()<<8);
|
||||
if (CmtLength<2)
|
||||
return false;
|
||||
CmtLength-=2;
|
||||
DataIO.SetCmt13Encryption();
|
||||
CommHead.UnpVer=15;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
UnpCmtLength=CommHead.UnpSize;
|
||||
DataIO.SetFiles(this,NULL);
|
||||
DataIO.EnableShowProgress(false);
|
||||
DataIO.SetPackedSizeToRead(CmtLength);
|
||||
DataIO.UnpHash.Init(HASH_CRC32,1);
|
||||
DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet.
|
||||
|
||||
Unpack CmtUnpack(&DataIO);
|
||||
CmtUnpack.Init(0x10000,false);
|
||||
CmtUnpack.SetDestSize(UnpCmtLength);
|
||||
CmtUnpack.DoUnpack(CommHead.UnpVer,false);
|
||||
|
||||
if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte *UnpData;
|
||||
size_t UnpDataSize;
|
||||
DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
|
||||
if (UnpDataSize>0)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// If we ever decide to extend it to Android, we'll need to alloc
|
||||
// 4x memory for OEM to UTF-8 output here.
|
||||
OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
|
||||
#endif
|
||||
std::string UnpStr((char*)UnpData,UnpDataSize);
|
||||
CharToWide(UnpStr,CmtData);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CmtLength==0)
|
||||
return false;
|
||||
std::vector<byte> CmtRaw(CmtLength);
|
||||
int ReadSize=Read(CmtRaw.data(),CmtLength);
|
||||
if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared.
|
||||
{
|
||||
CmtLength=ReadSize;
|
||||
CmtRaw.resize(CmtLength);
|
||||
}
|
||||
|
||||
if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
|
||||
{
|
||||
uiMsg(UIERROR_CMTBROKEN,FileName);
|
||||
return false;
|
||||
}
|
||||
// CmtData.resize(CmtLength+1);
|
||||
CmtRaw.push_back(0);
|
||||
#ifdef _WIN_ALL
|
||||
// If we ever decide to extend it to Android, we'll need to alloc
|
||||
// 4x memory for OEM to UTF-8 output here.
|
||||
OemToCharA((char *)CmtRaw.data(),(char *)CmtRaw.data());
|
||||
#endif
|
||||
CharToWide((const char *)CmtRaw.data(),CmtData);
|
||||
// CmtData->resize(wcslen(CmtData->data()));
|
||||
}
|
||||
#endif
|
||||
return CmtData.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::ReadCommentData(std::wstring &CmtData)
|
||||
{
|
||||
std::vector<byte> CmtRaw;
|
||||
if (!ReadSubData(&CmtRaw,NULL,false))
|
||||
return false;
|
||||
size_t CmtSize=CmtRaw.size();
|
||||
CmtRaw.push_back(0);
|
||||
// CmtData->resize(CmtSize+1);
|
||||
if (Format==RARFMT50)
|
||||
UtfToWide((char *)CmtRaw.data(),CmtData);
|
||||
else
|
||||
if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
|
||||
{
|
||||
CmtData=RawToWide(CmtRaw);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharToWide((const char *)CmtRaw.data(),CmtData);
|
||||
}
|
||||
// CmtData->resize(wcslen(CmtData->data())); // Set buffer size to actual comment length.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Archive::ViewComment()
|
||||
{
|
||||
if (Cmd->DisableComment)
|
||||
return;
|
||||
std::wstring CmtBuf;
|
||||
if (GetComment(CmtBuf)) // In GUI too, so "Test" command detects broken comments.
|
||||
{
|
||||
size_t CmtSize=CmtBuf.size();
|
||||
auto EndPos=CmtBuf.find(0x1A);
|
||||
if (EndPos!=std::wstring::npos)
|
||||
CmtSize=EndPos;
|
||||
mprintf(St(MArcComment));
|
||||
mprintf(L":\n");
|
||||
OutComment(CmtBuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+361
@@ -0,0 +1,361 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "arccmt.cpp"
|
||||
|
||||
|
||||
Archive::Archive(CommandData *InitCmd)
|
||||
{
|
||||
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
|
||||
|
||||
DummyCmd=(InitCmd==NULL);
|
||||
Cmd=DummyCmd ? (new CommandData):InitCmd;
|
||||
|
||||
OpenShared=Cmd->OpenShared;
|
||||
Format=RARFMT_NONE;
|
||||
Solid=false;
|
||||
Volume=false;
|
||||
MainComment=false;
|
||||
Locked=false;
|
||||
Signed=false;
|
||||
FirstVolume=false;
|
||||
NewNumbering=false;
|
||||
SFXSize=0;
|
||||
LatestTime.Reset();
|
||||
Protected=false;
|
||||
Encrypted=false;
|
||||
FailedHeaderDecryption=false;
|
||||
BrokenHeader=false;
|
||||
LastReadBlock=0;
|
||||
CurHeaderType=HEAD_UNKNOWN;
|
||||
|
||||
CurBlockPos=0;
|
||||
NextBlockPos=0;
|
||||
|
||||
RecoveryPercent=-1;
|
||||
|
||||
MainHead.Reset();
|
||||
CryptHead={};
|
||||
EndArcHead.Reset();
|
||||
|
||||
VolNumber=0;
|
||||
VolWrite=0;
|
||||
AddingFilesSize=0;
|
||||
AddingHeadersSize=0;
|
||||
|
||||
Splitting=false;
|
||||
NewArchive=false;
|
||||
|
||||
SilentOpen=false;
|
||||
|
||||
#ifdef USE_QOPEN
|
||||
ProhibitQOpen=false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
Archive::~Archive()
|
||||
{
|
||||
if (DummyCmd)
|
||||
delete Cmd;
|
||||
}
|
||||
|
||||
|
||||
void Archive::CheckArc(bool EnableBroken)
|
||||
{
|
||||
if (!IsArchive(EnableBroken))
|
||||
{
|
||||
// If FailedHeaderDecryption is set, we already reported that archive
|
||||
// password is incorrect.
|
||||
if (!FailedHeaderDecryption)
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
ErrHandler.Exit(RARX_BADARC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void Archive::CheckOpen(const std::wstring &Name)
|
||||
{
|
||||
TOpen(Name);
|
||||
CheckArc(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool Archive::WCheckOpen(const std::wstring &Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
return false;
|
||||
if (!IsArchive(false))
|
||||
{
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
|
||||
{
|
||||
RARFORMAT Type=RARFMT_NONE;
|
||||
if (Size>=1 && D[0]==0x52)
|
||||
#ifndef SFX_MODULE
|
||||
if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
|
||||
Type=RARFMT14;
|
||||
else
|
||||
#endif
|
||||
if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
|
||||
{
|
||||
// We check the last signature byte, so we can return a sensible
|
||||
// warning in case we'll want to change the archive format
|
||||
// sometimes in the future.
|
||||
#ifndef SFX_MODULE
|
||||
if (D[6]==0)
|
||||
Type=RARFMT15;
|
||||
else
|
||||
#endif
|
||||
if (D[6]==1)
|
||||
Type=RARFMT50;
|
||||
else
|
||||
if (D[6]>1 && D[6]<5)
|
||||
Type=RARFMT_FUTURE;
|
||||
}
|
||||
return Type;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::IsArchive(bool EnableBroken)
|
||||
{
|
||||
Encrypted=false;
|
||||
BrokenHeader=false; // Might be left from previous volume.
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
if (IsDevice())
|
||||
{
|
||||
uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
|
||||
return false;
|
||||
SFXSize=0;
|
||||
|
||||
RARFORMAT Type;
|
||||
if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14)
|
||||
Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<char> Buffer(MAXSFXSIZE);
|
||||
long CurPos=(long)Tell();
|
||||
int ReadSize=Read(Buffer.data(),Buffer.size()-16);
|
||||
for (int I=0;I<ReadSize;I++)
|
||||
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
|
||||
{
|
||||
char *D=&Buffer[28-CurPos];
|
||||
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
|
||||
continue;
|
||||
}
|
||||
SFXSize=CurPos+I;
|
||||
Seek(SFXSize,SEEK_SET);
|
||||
if (Format==RARFMT15 || Format==RARFMT50)
|
||||
Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
|
||||
break;
|
||||
}
|
||||
if (SFXSize==0)
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT_FUTURE)
|
||||
{
|
||||
uiMsg(UIERROR_NEWRARFORMAT,FileName);
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
|
||||
{
|
||||
if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
|
||||
return false;
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD5;
|
||||
}
|
||||
else
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD3;
|
||||
|
||||
#ifdef RARDLL
|
||||
// If callback function is not set, we cannot get the password,
|
||||
// so we skip the initial header processing for encrypted header archive.
|
||||
// It leads to skipped archive comment, but the rest of archive data
|
||||
// is processed correctly.
|
||||
if (Cmd->Callback==NULL)
|
||||
SilentOpen=true;
|
||||
#endif
|
||||
|
||||
bool HeadersLeft; // Any headers left to read.
|
||||
bool StartFound=false; // Main or encryption headers found.
|
||||
// Skip the archive encryption header if any and read the main header.
|
||||
while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
|
||||
{
|
||||
SeekToNext();
|
||||
|
||||
HEADER_TYPE Type=GetHeaderType();
|
||||
// In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
|
||||
// avoid the password prompt.
|
||||
StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
|
||||
if (StartFound)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// We should not do it for EnableBroken or we'll get 'not RAR archive'
|
||||
// messages when extracting encrypted archives with wrong password.
|
||||
if (FailedHeaderDecryption && !EnableBroken)
|
||||
return false;
|
||||
|
||||
if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
|
||||
{
|
||||
if (!FailedHeaderDecryption) // If not reported a wrong password already.
|
||||
uiMsg(UIERROR_MHEADERBROKEN,FileName);
|
||||
if (!EnableBroken)
|
||||
return false;
|
||||
}
|
||||
|
||||
MainComment=MainHead.CommentInHeader;
|
||||
|
||||
// If we process non-encrypted archive or can request a password,
|
||||
// we set 'first volume' flag based on file attributes below.
|
||||
// It is necessary for RAR 2.x archives, which did not have 'first volume'
|
||||
// flag in main header. Also for all RAR formats we need to scan until
|
||||
// first file header to set "comment" flag when reading service header.
|
||||
// Unless we are in silent mode, we need to know about presence of comment
|
||||
// immediately after IsArchive call.
|
||||
if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
|
||||
{
|
||||
int64 SavePos=Tell();
|
||||
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
|
||||
HEADER_TYPE SaveCurHeaderType=CurHeaderType;
|
||||
|
||||
while (ReadHeader()!=0)
|
||||
{
|
||||
HEADER_TYPE HeaderType=GetHeaderType();
|
||||
if (HeaderType==HEAD_SERVICE)
|
||||
{
|
||||
// If we have a split service headers, it surely indicates non-first
|
||||
// volume. But not split service header does not guarantee the first
|
||||
// volume, because we can have split file after non-split archive
|
||||
// comment. So we do not quit from loop here.
|
||||
FirstVolume=Volume && !SubHead.SplitBefore;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_FILE)
|
||||
{
|
||||
FirstVolume=Volume && !FileHead.SplitBefore;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
|
||||
break;
|
||||
SeekToNext();
|
||||
}
|
||||
CurBlockPos=SaveCurBlockPos;
|
||||
NextBlockPos=SaveNextBlockPos;
|
||||
CurHeaderType=SaveCurHeaderType;
|
||||
Seek(SavePos,SEEK_SET);
|
||||
}
|
||||
if (!Volume || FirstVolume)
|
||||
FirstVolumeName=FileName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Archive::SeekToNext()
|
||||
{
|
||||
Seek(NextBlockPos,SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Calculate the block size including encryption fields and padding if any.
|
||||
uint Archive::FullHeaderSize(size_t Size)
|
||||
{
|
||||
if (Encrypted)
|
||||
{
|
||||
Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
|
||||
if (Format == RARFMT50)
|
||||
Size += SIZE_INITV;
|
||||
else
|
||||
Size += SIZE_SALT30;
|
||||
}
|
||||
return uint(Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef USE_QOPEN
|
||||
bool Archive::Open(const std::wstring &Name,uint Mode)
|
||||
{
|
||||
// Important if we reuse Archive object and it has virtual QOpen
|
||||
// file position not matching real. For example, for 'l -v volname'.
|
||||
QOpen.Unload();
|
||||
|
||||
return File::Open(Name,Mode);
|
||||
}
|
||||
|
||||
|
||||
int Archive::Read(void *Data,size_t Size)
|
||||
{
|
||||
size_t Result;
|
||||
if (QOpen.Read(Data,Size,Result))
|
||||
return (int)Result;
|
||||
return File::Read(Data,Size);
|
||||
}
|
||||
|
||||
|
||||
void Archive::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!QOpen.Seek(Offset,Method))
|
||||
File::Seek(Offset,Method);
|
||||
}
|
||||
|
||||
|
||||
int64 Archive::Tell()
|
||||
{
|
||||
int64 QPos;
|
||||
if (QOpen.Tell(&QPos))
|
||||
return QPos;
|
||||
return File::Tell();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Return 0 if dictionary size is invalid. If size is RAR7 only, return
|
||||
// the adjusted nearest bottom value. Return header flags in Flags.
|
||||
uint64 Archive::GetWinSize(uint64 Size,uint &Flags)
|
||||
{
|
||||
Flags=0;
|
||||
// Allow 128 KB - 1 TB range.
|
||||
if (Size<0x20000 || Size>0x10000000000ULL)
|
||||
return 0;
|
||||
uint64 Pow2=0x20000; // Power of 2 dictionary size.
|
||||
for (;2*Pow2<=Size;Pow2*=2)
|
||||
Flags+=FCI_DICT_BIT0;
|
||||
if (Size==Pow2)
|
||||
return Size; // If 'Size' is the power of 2, return it as is.
|
||||
|
||||
// Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'.
|
||||
uint64 Fraction=(Size-Pow2)/(Pow2/32);
|
||||
Flags+=(uint)Fraction*FCI_DICT_FRACT0;
|
||||
return Pow2+Fraction*(Pow2/32);
|
||||
}
|
||||
Vendored
+158
@@ -0,0 +1,158 @@
|
||||
#ifndef _RAR_ARCHIVE_
|
||||
#define _RAR_ARCHIVE_
|
||||
|
||||
class PPack;
|
||||
class RawRead;
|
||||
class RawWrite;
|
||||
|
||||
enum NOMODIFY_FLAGS
|
||||
{
|
||||
NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
|
||||
};
|
||||
|
||||
enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
|
||||
|
||||
enum ADDSUBDATA_FLAGS
|
||||
{
|
||||
ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
|
||||
ASDF_COMPRESS = 2, // Allow to compress data following subheader.
|
||||
ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
|
||||
ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
|
||||
};
|
||||
|
||||
// RAR5 headers must not exceed 2 MB.
|
||||
#define MAX_HEADER_SIZE_RAR5 0x200000
|
||||
|
||||
class Archive:public File
|
||||
{
|
||||
private:
|
||||
void UpdateLatestTime(FileHeader *CurBlock);
|
||||
void ConvertNameCase(std::wstring &Name);
|
||||
void ConvertFileHeader(FileHeader *hd);
|
||||
size_t ReadHeader14();
|
||||
size_t ReadHeader15();
|
||||
size_t ReadHeader50();
|
||||
void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb);
|
||||
void RequestArcPassword(RarCheckPassword *SelPwd);
|
||||
void UnexpEndArcMsg();
|
||||
void BrokenHeaderMsg();
|
||||
void UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info);
|
||||
bool DoGetComment(std::wstring &CmtData);
|
||||
bool ReadCommentData(std::wstring &CmtData);
|
||||
|
||||
#if !defined(RAR_NOCRYPT)
|
||||
CryptData HeadersCrypt;
|
||||
#endif
|
||||
ComprDataIO SubDataIO;
|
||||
bool DummyCmd;
|
||||
CommandData *Cmd;
|
||||
|
||||
int RecoveryPercent;
|
||||
|
||||
RarTime LatestTime;
|
||||
int LastReadBlock;
|
||||
HEADER_TYPE CurHeaderType;
|
||||
|
||||
bool SilentOpen;
|
||||
#ifdef USE_QOPEN
|
||||
QuickOpen QOpen;
|
||||
bool ProhibitQOpen;
|
||||
#endif
|
||||
public:
|
||||
Archive(CommandData *InitCmd=nullptr);
|
||||
~Archive();
|
||||
static RARFORMAT IsSignature(const byte *D,size_t Size);
|
||||
bool IsArchive(bool EnableBroken);
|
||||
size_t SearchBlock(HEADER_TYPE HeaderType);
|
||||
size_t SearchSubBlock(const wchar *Type);
|
||||
size_t SearchRR();
|
||||
int GetRecoveryPercent() {return RecoveryPercent;}
|
||||
size_t ReadHeader();
|
||||
void CheckArc(bool EnableBroken);
|
||||
void CheckOpen(const std::wstring &Name);
|
||||
bool WCheckOpen(const std::wstring &Name);
|
||||
bool GetComment(std::wstring &CmtData);
|
||||
void ViewComment();
|
||||
void SetLatestTime(RarTime *NewTime);
|
||||
void SeekToNext();
|
||||
bool CheckAccess();
|
||||
bool IsArcDir();
|
||||
void ConvertAttributes();
|
||||
void VolSubtractHeaderSize(size_t SubSize);
|
||||
uint FullHeaderSize(size_t Size);
|
||||
int64 GetStartPos();
|
||||
void AddSubData(const byte *SrcData,uint64 DataSize,File *SrcFile,
|
||||
const wchar *Name,uint Flags);
|
||||
bool ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode);
|
||||
HEADER_TYPE GetHeaderType() {return CurHeaderType;}
|
||||
CommandData* GetCommandData() {return Cmd;}
|
||||
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
|
||||
#ifdef USE_QOPEN
|
||||
bool Open(const std::wstring &Name,uint Mode=FMF_READ) override;
|
||||
int Read(void *Data,size_t Size) override;
|
||||
void Seek(int64 Offset,int Method) override;
|
||||
int64 Tell() override;
|
||||
void QOpenUnload() {QOpen.Unload();}
|
||||
void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
|
||||
#endif
|
||||
static uint64 GetWinSize(uint64 Size,uint &Flags);
|
||||
|
||||
// Needed to see wstring based Open from File. Otherwise compiler finds
|
||||
// Open in Archive and doesn't check the base class overloads.
|
||||
using File::Open;
|
||||
|
||||
BaseBlock ShortBlock;
|
||||
MarkHeader MarkHead;
|
||||
MainHeader MainHead;
|
||||
CryptHeader CryptHead;
|
||||
FileHeader FileHead;
|
||||
EndArcHeader EndArcHead;
|
||||
SubBlockHeader SubBlockHead;
|
||||
FileHeader SubHead;
|
||||
CommentHeader CommHead;
|
||||
ProtectHeader ProtectHead;
|
||||
EAHeader EAHead;
|
||||
StreamHeader StreamHead;
|
||||
|
||||
int64 CurBlockPos;
|
||||
int64 NextBlockPos;
|
||||
|
||||
RARFORMAT Format;
|
||||
bool Solid;
|
||||
bool Volume;
|
||||
bool MainComment;
|
||||
bool Locked;
|
||||
bool Signed;
|
||||
bool FirstVolume;
|
||||
bool NewNumbering;
|
||||
bool Protected;
|
||||
bool Encrypted;
|
||||
size_t SFXSize;
|
||||
bool BrokenHeader;
|
||||
bool FailedHeaderDecryption;
|
||||
|
||||
#if !defined(RAR_NOCRYPT)
|
||||
byte ArcSalt[SIZE_SALT50];
|
||||
#endif
|
||||
|
||||
bool Splitting;
|
||||
|
||||
uint VolNumber;
|
||||
int64 VolWrite;
|
||||
|
||||
// Total size of files adding to archive. Might also include the size of
|
||||
// files repacked in solid archive.
|
||||
uint64 AddingFilesSize;
|
||||
|
||||
uint64 AddingHeadersSize;
|
||||
|
||||
bool NewArchive;
|
||||
|
||||
std::wstring FirstVolumeName;
|
||||
#ifdef PROPAGATE_MOTW
|
||||
MarkOfTheWeb Motw;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+1553
File diff suppressed because it is too large.
Load diff
Vendored
+179
@@ -0,0 +1,179 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
static const byte blake2s_sigma[10][16] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
|
||||
};
|
||||
|
||||
#ifdef USE_SSE
|
||||
#include "blake2s_sse.cpp"
|
||||
#endif
|
||||
|
||||
static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
|
||||
static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
|
||||
static void blake2s_final( blake2s_state *S, byte *digest );
|
||||
|
||||
#include "blake2sp.cpp"
|
||||
|
||||
static const uint32 blake2s_IV[8] =
|
||||
{
|
||||
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||
};
|
||||
|
||||
static inline void blake2s_set_lastnode( blake2s_state *S )
|
||||
{
|
||||
S->f[1] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
/* Some helper functions, not necessarily useful */
|
||||
static inline void blake2s_set_lastblock( blake2s_state *S )
|
||||
{
|
||||
if( S->last_node ) blake2s_set_lastnode( S );
|
||||
|
||||
S->f[0] = ~0U;
|
||||
}
|
||||
|
||||
|
||||
static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
|
||||
{
|
||||
S->t[0] += inc;
|
||||
S->t[1] += ( S->t[0] < inc );
|
||||
}
|
||||
|
||||
|
||||
/* init2 xors IV with input parameter block */
|
||||
void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
if (_SSE_Version>=SSE_SSE2)
|
||||
blake2s_init_sse();
|
||||
#endif
|
||||
|
||||
S->init(); // Clean data.
|
||||
for( int i = 0; i < 8; ++i )
|
||||
S->h[i] = blake2s_IV[i];
|
||||
|
||||
S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
|
||||
S->h[2] ^= node_offset;
|
||||
S->h[3] ^= (node_depth<<16)|0x20000000;
|
||||
}
|
||||
|
||||
|
||||
#define G(r,i,m,a,b,c,d) \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
|
||||
d = rotr32(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 12); \
|
||||
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
|
||||
d = rotr32(d ^ a, 8); \
|
||||
c = c + d; \
|
||||
b = rotr32(b ^ c, 7);
|
||||
|
||||
|
||||
static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
uint32 m[16];
|
||||
uint32 v[16];
|
||||
|
||||
for( size_t i = 0; i < 16; ++i )
|
||||
m[i] = RawGet4( block + i * 4 );
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
v[i] = S->h[i];
|
||||
|
||||
v[ 8] = blake2s_IV[0];
|
||||
v[ 9] = blake2s_IV[1];
|
||||
v[10] = blake2s_IV[2];
|
||||
v[11] = blake2s_IV[3];
|
||||
v[12] = S->t[0] ^ blake2s_IV[4];
|
||||
v[13] = S->t[1] ^ blake2s_IV[5];
|
||||
v[14] = S->f[0] ^ blake2s_IV[6];
|
||||
v[15] = S->f[1] ^ blake2s_IV[7];
|
||||
|
||||
for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
|
||||
{
|
||||
G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
|
||||
G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
|
||||
G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
|
||||
G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
|
||||
G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
|
||||
G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
|
||||
G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
|
||||
G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 8; ++i )
|
||||
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
|
||||
void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
while( inlen > 0 )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
|
||||
|
||||
if( inlen > fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill ); // Fill buffer
|
||||
S->buflen += fill;
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
#ifdef USE_SSE
|
||||
if (_SSE_Version>=SSE_SSSE3)
|
||||
blake2s_compress_sse( S, S->buf );
|
||||
else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#else
|
||||
blake2s_compress( S, S->buf ); // Compress
|
||||
#endif
|
||||
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
}
|
||||
else // inlen <= fill
|
||||
{
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
S->buflen += (size_t)inlen; // Be lazy, do not compress
|
||||
in += inlen;
|
||||
inlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blake2s_final( blake2s_state *S, byte *digest )
|
||||
{
|
||||
if( S->buflen > BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
|
||||
blake2s_compress( S, S->buf );
|
||||
S->buflen -= BLAKE2S_BLOCKBYTES;
|
||||
memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
|
||||
}
|
||||
|
||||
blake2s_increment_counter( S, ( uint32 )S->buflen );
|
||||
blake2s_set_lastblock( S );
|
||||
memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
|
||||
blake2s_compress( S, S->buf );
|
||||
|
||||
for( int i = 0; i < 8; ++i ) /* Output full hash */
|
||||
RawPut4( S->h[i], digest + 4 * i );
|
||||
}
|
||||
|
||||
Vendored
+104
@@ -0,0 +1,104 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
#ifndef _RAR_BLAKE2_
|
||||
#define _RAR_BLAKE2_
|
||||
|
||||
#define BLAKE2_DIGEST_SIZE 32
|
||||
#define BLAKE2_THREADS_NUMBER 8
|
||||
|
||||
// Use constexpr instead of enums for -std=c++20 compatibility.
|
||||
constexpr size_t BLAKE2S_BLOCKBYTES = 64;
|
||||
constexpr size_t BLAKE2S_OUTBYTES = 32;
|
||||
|
||||
// Alignment to 64 improves performance of both SSE and non-SSE versions.
|
||||
// Alignment to n*16 is required for SSE version, so we selected 64.
|
||||
// We use the custom alignment scheme instead of __declspec(align(x)),
|
||||
// because it is less compiler dependent. Also the compiler directive
|
||||
// does not help if structure is a member of class allocated through
|
||||
// 'new' operator.
|
||||
struct blake2s_state
|
||||
{
|
||||
// Use constexpr instead of enums, because otherwise clang -std=c++20
|
||||
// issues a warning about "arithmetic between different enumeration types"
|
||||
// in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration.
|
||||
static constexpr size_t BLAKE_ALIGNMENT = 64;
|
||||
|
||||
// buffer and uint32 h[8], t[2], f[2];
|
||||
// 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip.
|
||||
// It might differ in later versions.
|
||||
static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
|
||||
|
||||
byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
|
||||
uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
|
||||
|
||||
size_t buflen;
|
||||
byte last_node;
|
||||
|
||||
blake2s_state()
|
||||
{
|
||||
set_pointers();
|
||||
}
|
||||
|
||||
// Required when we declare and assign in the same command.
|
||||
blake2s_state(blake2s_state &st)
|
||||
{
|
||||
set_pointers();
|
||||
*this=st;
|
||||
}
|
||||
|
||||
void set_pointers()
|
||||
{
|
||||
// Set aligned pointers. Must be done in constructor, not in Init(),
|
||||
// so assignments like 'blake2sp_state res=blake2ctx' work correctly
|
||||
// even if blake2sp_init is not called for 'res'.
|
||||
buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
|
||||
h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
|
||||
t = h + 8;
|
||||
f = t + 2;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
memset( ubuf, 0, sizeof( ubuf ) );
|
||||
buflen = 0;
|
||||
last_node = 0;
|
||||
}
|
||||
|
||||
// Since we use pointers, the default = would work incorrectly.
|
||||
blake2s_state& operator = (blake2s_state &st)
|
||||
{
|
||||
if (this != &st)
|
||||
{
|
||||
memcpy(buf, st.buf, BLAKE_DATA_SIZE);
|
||||
buflen = st.buflen;
|
||||
last_node = st.last_node;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
class ThreadPool;
|
||||
#endif
|
||||
|
||||
struct blake2sp_state
|
||||
{
|
||||
blake2s_state S[8];
|
||||
blake2s_state R;
|
||||
byte buf[8 * BLAKE2S_BLOCKBYTES];
|
||||
size_t buflen;
|
||||
|
||||
#ifdef RAR_SMP
|
||||
ThreadPool *ThPool;
|
||||
uint MaxThreads;
|
||||
#endif
|
||||
};
|
||||
|
||||
void blake2sp_init( blake2sp_state *S );
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest );
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
// Based on public domain code written in 2012 by Samuel Neves
|
||||
|
||||
// Initialization vector.
|
||||
static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
|
||||
|
||||
// Constants for cyclic rotation.
|
||||
static __m128i crotr8, crotr16;
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((target("sse2")))
|
||||
#endif
|
||||
static void blake2s_init_sse()
|
||||
{
|
||||
// We cannot initialize these 128 bit variables in place when declaring
|
||||
// them globally, because global scope initialization is performed before
|
||||
// our SSE check and it would make code incompatible with older non-SSE2
|
||||
// CPUs. Also we cannot initialize them as static inside of function
|
||||
// using these variables, because SSE static initialization is not thread
|
||||
// safe: first thread starts initialization and sets "init done" flag even
|
||||
// if it is not done yet, second thread can attempt to access half-init
|
||||
// SSE data. So we moved init code here.
|
||||
|
||||
blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
|
||||
blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
|
||||
|
||||
crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
|
||||
crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
|
||||
}
|
||||
|
||||
|
||||
#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
|
||||
#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
|
||||
|
||||
#define mm_rotr_epi32(r, c) ( \
|
||||
c==8 ? _mm_shuffle_epi8(r,crotr8) \
|
||||
: c==16 ? _mm_shuffle_epi8(r,crotr16) \
|
||||
: _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
|
||||
|
||||
#define G1(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 16); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 12);
|
||||
|
||||
#define G2(row1,row2,row3,row4,buf) \
|
||||
row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
|
||||
row4 = _mm_xor_si128( row4, row1 ); \
|
||||
row4 = mm_rotr_epi32(row4, 8); \
|
||||
row3 = _mm_add_epi32( row3, row4 ); \
|
||||
row2 = _mm_xor_si128( row2, row3 ); \
|
||||
row2 = mm_rotr_epi32(row2, 7);
|
||||
|
||||
#define DIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
|
||||
|
||||
#define UNDIAGONALIZE(row1,row2,row3,row4) \
|
||||
row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
|
||||
row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
|
||||
row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
|
||||
|
||||
// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
|
||||
// and about the same in x64 mode in our test. Perhaps depends on compiler.
|
||||
// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
|
||||
// instructions here, but they did not show any speed gain on i7-6700K.
|
||||
#define SSE_ROUND(m,row,r) \
|
||||
{ \
|
||||
__m128i buf; \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
DIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
|
||||
G1(row[0],row[1],row[2],row[3],buf); \
|
||||
buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
|
||||
G2(row[0],row[1],row[2],row[3],buf); \
|
||||
UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
|
||||
}
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((target("ssse3")))
|
||||
#endif
|
||||
static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
|
||||
{
|
||||
__m128i row[4];
|
||||
__m128i ff0, ff1;
|
||||
|
||||
const uint32 *m = ( uint32 * )block;
|
||||
|
||||
row[0] = ff0 = LOAD( &S->h[0] );
|
||||
row[1] = ff1 = LOAD( &S->h[4] );
|
||||
|
||||
row[2] = blake2s_IV_0_3;
|
||||
row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
|
||||
SSE_ROUND( m, row, 0 );
|
||||
SSE_ROUND( m, row, 1 );
|
||||
SSE_ROUND( m, row, 2 );
|
||||
SSE_ROUND( m, row, 3 );
|
||||
SSE_ROUND( m, row, 4 );
|
||||
SSE_ROUND( m, row, 5 );
|
||||
SSE_ROUND( m, row, 6 );
|
||||
SSE_ROUND( m, row, 7 );
|
||||
SSE_ROUND( m, row, 8 );
|
||||
SSE_ROUND( m, row, 9 );
|
||||
STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
|
||||
STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
|
||||
return 0;
|
||||
}
|
||||
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
BLAKE2 reference source code package - reference C implementations
|
||||
|
||||
Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along with
|
||||
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
|
||||
#define PARALLELISM_DEGREE 8
|
||||
|
||||
void blake2sp_init( blake2sp_state *S )
|
||||
{
|
||||
memset( S->buf, 0, sizeof( S->buf ) );
|
||||
S->buflen = 0;
|
||||
|
||||
blake2s_init_param( &S->R, 0, 1 ); // Init root.
|
||||
|
||||
for( uint32 i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
|
||||
|
||||
S->R.last_node = 1;
|
||||
S->S[PARALLELISM_DEGREE - 1].last_node = 1;
|
||||
}
|
||||
|
||||
|
||||
struct Blake2ThreadData
|
||||
{
|
||||
void Update();
|
||||
blake2s_state *S;
|
||||
const byte *in;
|
||||
size_t inlen;
|
||||
};
|
||||
|
||||
|
||||
void Blake2ThreadData::Update()
|
||||
{
|
||||
size_t inlen__ = inlen;
|
||||
const byte *in__ = ( const byte * )in;
|
||||
|
||||
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
#ifdef USE_SSE
|
||||
// We gain 5% in i7 SSE mode by prefetching next data block.
|
||||
if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
|
||||
_mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
|
||||
#endif
|
||||
// We tried to _forceinline blake2s_update and blake2s_compress_sse,
|
||||
// but it didn't improve performance.
|
||||
blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
|
||||
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RAR_SMP
|
||||
THREAD_PROC(Blake2Thread)
|
||||
{
|
||||
Blake2ThreadData *td=(Blake2ThreadData *)Data;
|
||||
td->Update();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
|
||||
{
|
||||
size_t left = S->buflen;
|
||||
size_t fill = sizeof( S->buf ) - left;
|
||||
|
||||
if( left && inlen >= fill )
|
||||
{
|
||||
memcpy( S->buf + left, in, fill );
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
|
||||
|
||||
in += fill;
|
||||
inlen -= fill;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
Blake2ThreadData btd_array[PARALLELISM_DEGREE];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
|
||||
|
||||
if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
|
||||
ThreadNumber=4;
|
||||
#else
|
||||
uint ThreadNumber=1;
|
||||
#endif
|
||||
|
||||
for (size_t id__=0;id__<PARALLELISM_DEGREE;)
|
||||
{
|
||||
for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++)
|
||||
{
|
||||
Blake2ThreadData *btd=btd_array+Thread;
|
||||
|
||||
btd->inlen = inlen;
|
||||
btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
|
||||
btd->S = &S->S[id__];
|
||||
|
||||
#ifdef RAR_SMP
|
||||
if (ThreadNumber>1)
|
||||
S->ThPool->AddTask(Blake2Thread,(void*)btd);
|
||||
else
|
||||
btd->Update();
|
||||
#else
|
||||
btd->Update();
|
||||
#endif
|
||||
id__++;
|
||||
}
|
||||
#ifdef RAR_SMP
|
||||
if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
|
||||
S->ThPool->WaitDone();
|
||||
#endif // RAR_SMP
|
||||
}
|
||||
|
||||
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
|
||||
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( inlen > 0 )
|
||||
memcpy( S->buf + left, in, (size_t)inlen );
|
||||
|
||||
S->buflen = left + (size_t)inlen;
|
||||
}
|
||||
|
||||
|
||||
void blake2sp_final( blake2sp_state *S, byte *digest )
|
||||
{
|
||||
byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
{
|
||||
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
|
||||
{
|
||||
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
|
||||
|
||||
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
|
||||
|
||||
blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
|
||||
}
|
||||
|
||||
blake2s_final( &S->S[i], hash[i] );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
|
||||
blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
|
||||
|
||||
blake2s_final( &S->R, digest );
|
||||
}
|
||||
Vendored
+1330
File diff suppressed because it is too large.
Load diff
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef _RAR_CMDDATA_
|
||||
#define _RAR_CMDDATA_
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
// In Windows we implement our own command line parser to avoid replacing
|
||||
// \" by " in standard parser. Such replacing corrupts destination paths
|
||||
// like "dest path\" in extraction commands.
|
||||
#define CUSTOM_CMDLINE_PARSER
|
||||
#endif
|
||||
|
||||
#define DefaultStoreList L"7z;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst"
|
||||
|
||||
enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
|
||||
|
||||
enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1};
|
||||
|
||||
class CommandData:public RAROptions
|
||||
{
|
||||
private:
|
||||
void ProcessSwitch(const wchar *Switch);
|
||||
void BadSwitch(const wchar *Switch);
|
||||
uint GetExclAttr(const wchar *Str,bool &Dir);
|
||||
#if !defined(SFX_MODULE)
|
||||
void SetTimeFilters(const wchar *Mod,bool Before,bool Age);
|
||||
void SetStoreTimeMode(const wchar *S);
|
||||
#endif
|
||||
int64 GetModSize(const wchar *S,uint DefMultiplier);
|
||||
|
||||
bool FileLists;
|
||||
bool NoMoreSwitches;
|
||||
RAR_CMD_LIST_MODE ListMode;
|
||||
bool BareOutput;
|
||||
public:
|
||||
CommandData();
|
||||
void Init();
|
||||
|
||||
void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
|
||||
void ParseArg(const wchar *ArgW);
|
||||
void ParseDone();
|
||||
void ParseEnvVar();
|
||||
void ReadConfig();
|
||||
void PreprocessArg(const wchar *Arg);
|
||||
void ProcessSwitchesString(const std::wstring &Str);
|
||||
void OutTitle();
|
||||
void OutHelp(RAR_EXIT ExitCode);
|
||||
bool IsSwitch(int Ch);
|
||||
bool ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
|
||||
static bool CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode);
|
||||
bool ExclDirByAttr(uint FileAttr);
|
||||
bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta);
|
||||
bool SizeCheck(int64 Size);
|
||||
bool AnyFiltersActive();
|
||||
int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
|
||||
bool Flags,std::wstring *MatchedArg);
|
||||
void ProcessCommand();
|
||||
void AddArcName(const std::wstring &Name);
|
||||
bool GetArcName(wchar *Name,int MaxSize);
|
||||
bool GetArcName(std::wstring &Name);
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
void ReportWrongSwitches(RARFORMAT Format);
|
||||
#endif
|
||||
|
||||
void GetBriefMaskList(const std::wstring &Masks,StringList &Args);
|
||||
|
||||
|
||||
std::wstring Command;
|
||||
std::wstring ArcName;
|
||||
std::wstring ExtrPath;
|
||||
std::wstring TempPath;
|
||||
std::wstring SFXModule;
|
||||
std::wstring CommentFile;
|
||||
std::wstring ArcPath; // For -ap<path>.
|
||||
std::wstring ExclArcPath; // For -ep4<path> switch.
|
||||
std::wstring LogName;
|
||||
std::wstring EmailTo;
|
||||
|
||||
// Read data from stdin and store in archive under a name specified here
|
||||
// when archiving. Read an archive from stdin if any non-empty string
|
||||
// is specified here when extracting.
|
||||
std::wstring UseStdin;
|
||||
|
||||
StringList FileArgs;
|
||||
StringList ExclArgs;
|
||||
StringList InclArgs;
|
||||
StringList ArcNames;
|
||||
StringList StoreArgs;
|
||||
#ifdef PROPAGATE_MOTW
|
||||
StringList MotwList; // Extensions to assign the mark of the web.
|
||||
#endif
|
||||
|
||||
SecPassword Password;
|
||||
|
||||
std::vector<int64> NextVolSizes;
|
||||
|
||||
|
||||
#ifdef RARDLL
|
||||
std::wstring DllDestName;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+380
@@ -0,0 +1,380 @@
|
||||
// Return 'true' if we need to exclude the file from processing as result
|
||||
// of -x switch. If CheckInclList is true, we also check the file against
|
||||
// the include list created with -n switch.
|
||||
bool CommandData::ExclCheck(const std::wstring &CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
|
||||
{
|
||||
if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
|
||||
return true;
|
||||
if (!CheckInclList || InclArgs.ItemsCount()==0)
|
||||
return false;
|
||||
if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CommandData::CheckArgs(StringList *Args,bool Dir,const std::wstring &CheckName,bool CheckFullPath,int MatchMode)
|
||||
{
|
||||
std::wstring Name,FullName,CurMask;
|
||||
ConvertPath(&CheckName,&Name);
|
||||
Args->Rewind();
|
||||
while (Args->GetString(CurMask))
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// 2025.09.11: Unix allows DOS slashes as a part of file name, so we do not
|
||||
// convert it for Unix. In Windows we wish -xdir\file and -xdir/file both
|
||||
// to exclude the file.
|
||||
UnixSlashToDos(CurMask,CurMask);
|
||||
#endif
|
||||
wchar LastMaskChar=GetLastChar(CurMask);
|
||||
bool DirMask=IsPathDiv(LastMaskChar); // Mask for directories only.
|
||||
|
||||
if (Dir)
|
||||
{
|
||||
// CheckName is a directory.
|
||||
if (DirMask)
|
||||
{
|
||||
// We process the directory and have the directory exclusion mask.
|
||||
// So let's convert "mask\" to "mask" and process it normally.
|
||||
|
||||
CurMask.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
// This code doesn't allow to apply -n and -x wildcard masks without
|
||||
// trailing slash to folders unless these masks are * and *.*.
|
||||
// See the changes history below.
|
||||
// 2023.03.26: Previously we removed this code completely to let
|
||||
// 'rar a arc dir -ndir\path\*' include empty folders in 'path' too.
|
||||
// But then we received an email from user not willing -x*.avi to
|
||||
// exclude folders like dir.avi with non-avi files. Also rar.txt
|
||||
// mentions that masks like *.avi exclude only files. Initially
|
||||
// we wanted masks like -npath\* or -xpath\* to match the entire
|
||||
// contents of path including empty folders and added the special
|
||||
// check for "*" and "*.*". But this is not very straightforward,
|
||||
// when *.* and *.avi are processed differently, especially taking
|
||||
// into account that we can specify the exact folder name without
|
||||
// wildcards to process it and masks like 'dir*\' can be used to
|
||||
// exclude folders. So we decided to skip all usual wildcard masks
|
||||
// for folders.
|
||||
// 2023.11.22: We returned the special check for "*" and "*.*",
|
||||
// because users expected 'rar a arc dir -xdir\*' to exclude
|
||||
// everything including subfolders in 'dir'. For now we returned it
|
||||
// both for -n and -x, but we can limit it to -x only if needed.
|
||||
std::wstring Name=PointToName(CurMask);
|
||||
if (IsWildcard(Name) && Name!=L"*" && Name!=L"*.*")
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we process a file inside of directory excluded by "dirmask\".
|
||||
// we want to exclude such file too. So we convert "dirmask\" to
|
||||
// "dirmask\*". It is important for operations other than archiving
|
||||
// with -x. When archiving with -x, directory matched by "dirmask\"
|
||||
// is excluded from further scanning.
|
||||
|
||||
if (DirMask)
|
||||
CurMask+=L"*";
|
||||
}
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
if (CheckFullPath && IsFullPath(CurMask))
|
||||
{
|
||||
// We do not need to do the special "*\" processing here, because
|
||||
// unlike the "else" part of this "if", now we convert names to full
|
||||
// format, so they all include the path, which is matched by "*\"
|
||||
// correctly. Moreover, removing "*\" from mask would break
|
||||
// the comparison, because now all names have the path.
|
||||
|
||||
if (FullName.empty())
|
||||
ConvertNameToFull(CheckName,FullName);
|
||||
if (CmpName(CurMask,FullName,MatchMode))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
std::wstring CurName=Name;
|
||||
|
||||
// Important to convert before "*\" check below, so masks like
|
||||
// d:*\something are processed properly.
|
||||
size_t MaskOffset=ConvertPath(&CurMask,nullptr);
|
||||
std::wstring CmpMask=CurMask.substr(MaskOffset);
|
||||
|
||||
if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
|
||||
{
|
||||
// We want "*\name" to match 'name' not only in subdirectories,
|
||||
// but also in the current directory. We convert the name
|
||||
// from 'name' to '.\name' to be matched by "*\" part even if it is
|
||||
// in current directory.
|
||||
CurName=L'.';
|
||||
CurName+=CPATHDIVIDER;
|
||||
CurName+=Name;
|
||||
}
|
||||
|
||||
if (CmpName(CmpMask,CurName,MatchMode))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// Now this function performs only one task and only in Windows version:
|
||||
// it skips symlinks to directories if -e1024 switch is specified.
|
||||
// Symlinks are skipped in ScanTree class, so their entire contents
|
||||
// is skipped too. Without this function we would check the attribute
|
||||
// only directly before archiving, so we would skip the symlink record,
|
||||
// but not the contents of symlinked directory.
|
||||
bool CommandData::ExclDirByAttr(uint FileAttr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
|
||||
(ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age)
|
||||
{
|
||||
bool ModeOR=false,TimeMods=false;
|
||||
const wchar *S=Mod;
|
||||
// Check if any 'mca' modifiers are present, set OR mode if 'o' is present,
|
||||
// skip modifiers and set S to beginning of time string. Be sure to check
|
||||
// *S!=0, because termination 0 is a part of string for wcschr.
|
||||
for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++)
|
||||
if (*S=='o' || *S=='O')
|
||||
ModeOR=true;
|
||||
else
|
||||
TimeMods=true;
|
||||
|
||||
if (!TimeMods) // Assume 'm' if no modifiers are specified.
|
||||
Mod=L"m";
|
||||
|
||||
// Set the specified time for every modifier. Be sure to check *Mod!=0,
|
||||
// because termination 0 is a part of string for wcschr. This check is
|
||||
// important when we set Mod to "m" above.
|
||||
for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++)
|
||||
switch(toupperw(*Mod))
|
||||
{
|
||||
case 'M':
|
||||
if (Before)
|
||||
{
|
||||
Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S);
|
||||
FileMtimeBeforeOR=ModeOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S);
|
||||
FileMtimeAfterOR=ModeOR;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
if (Before)
|
||||
{
|
||||
Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S);
|
||||
FileCtimeBeforeOR=ModeOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S);
|
||||
FileCtimeAfterOR=ModeOR;
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
if (Before)
|
||||
{
|
||||
Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S);
|
||||
FileAtimeBeforeOR=ModeOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S);
|
||||
FileAtimeAfterOR=ModeOR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// Return 'true' if we need to exclude the file from processing.
|
||||
bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
|
||||
{
|
||||
bool FilterOR=false;
|
||||
|
||||
if (FileMtimeBefore.IsSet()) // Filter present.
|
||||
if (ftm>=FileMtimeBefore) // Condition not matched.
|
||||
if (FileMtimeBeforeOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileMtimeBeforeOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
if (FileMtimeAfter.IsSet()) // Filter present.
|
||||
if (ftm<FileMtimeAfter) // Condition not matched.
|
||||
if (FileMtimeAfterOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileMtimeAfterOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
if (FileCtimeBefore.IsSet()) // Filter present.
|
||||
if (ftc>=FileCtimeBefore) // Condition not matched.
|
||||
if (FileCtimeBeforeOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileCtimeBeforeOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
if (FileCtimeAfter.IsSet()) // Filter present.
|
||||
if (ftc<FileCtimeAfter) // Condition not matched.
|
||||
if (FileCtimeAfterOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileCtimeAfterOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
if (FileAtimeBefore.IsSet()) // Filter present.
|
||||
if (fta>=FileAtimeBefore) // Condition not matched.
|
||||
if (FileAtimeBeforeOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileAtimeBeforeOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
if (FileAtimeAfter.IsSet()) // Filter present.
|
||||
if (fta<FileAtimeAfter) // Condition not matched.
|
||||
if (FileAtimeAfterOR)
|
||||
FilterOR=true; // Not matched OR filter is present.
|
||||
else
|
||||
return true; // Exclude file in AND mode.
|
||||
else // Condition matched.
|
||||
if (FileAtimeAfterOR)
|
||||
return false; // Include file in OR mode.
|
||||
|
||||
return FilterOR; // Exclude if all OR filters are not matched.
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// Return 'true' if we need to exclude the file from processing.
|
||||
bool CommandData::SizeCheck(int64 Size)
|
||||
{
|
||||
if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size.
|
||||
return false;
|
||||
if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
|
||||
return true;
|
||||
if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// Return 0 if file must not be processed or a number of matched parameter otherwise.
|
||||
int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
|
||||
bool Flags,std::wstring *MatchedArg)
|
||||
{
|
||||
if (MatchedArg!=NULL)
|
||||
MatchedArg->clear();
|
||||
bool Dir=FileHead.Dir;
|
||||
if (ExclCheck(FileHead.FileName,Dir,false,true))
|
||||
return 0;
|
||||
#ifndef SFX_MODULE
|
||||
if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime))
|
||||
return 0;
|
||||
if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
|
||||
return 0;
|
||||
if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 &&
|
||||
(!FileHead.Dir || !InclDir))
|
||||
return 0;
|
||||
if (!Dir && SizeCheck(FileHead.UnpSize))
|
||||
return 0;
|
||||
#endif
|
||||
std::wstring ArgName;
|
||||
FileArgs.Rewind();
|
||||
for (int StringCount=1;FileArgs.GetString(ArgName);StringCount++)
|
||||
{
|
||||
// Ensure that both parameters of CmpName are either C++ strings or
|
||||
// pointers, so we avoid time consuming string construction for one of
|
||||
// parameters in this expensive loop.
|
||||
if (CmpName(ArgName,FileHead.FileName,MatchType))
|
||||
{
|
||||
if (ExactMatch!=NULL)
|
||||
*ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
|
||||
if (MatchedArg!=NULL)
|
||||
*MatchedArg=ArgName;
|
||||
return StringCount;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void CommandData::SetStoreTimeMode(const wchar *S)
|
||||
{
|
||||
if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+')
|
||||
{
|
||||
// Apply -ts, -ts1, -ts-, -ts+ to all 3 times.
|
||||
// Handle obsolete -ts[2,3,4] as ts+.
|
||||
EXTTIME_MODE Mode=EXTTIME_MAX;
|
||||
if (*S=='-')
|
||||
Mode=EXTTIME_NONE;
|
||||
if (*S=='1')
|
||||
Mode=EXTTIME_1S;
|
||||
xmtime=xctime=xatime=Mode;
|
||||
S++;
|
||||
}
|
||||
|
||||
while (*S!=0)
|
||||
{
|
||||
EXTTIME_MODE Mode=EXTTIME_MAX;
|
||||
if (S[1]=='-')
|
||||
Mode=EXTTIME_NONE;
|
||||
if (S[1]=='1')
|
||||
Mode=EXTTIME_1S;
|
||||
switch(toupperw(*S))
|
||||
{
|
||||
case 'M':
|
||||
xmtime=Mode;
|
||||
break;
|
||||
case 'C':
|
||||
xctime=Mode;
|
||||
break;
|
||||
case 'A':
|
||||
xatime=Mode;
|
||||
break;
|
||||
case 'P':
|
||||
PreserveAtime=true;
|
||||
break;
|
||||
}
|
||||
S++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
void CommandData::OutTitle()
|
||||
{
|
||||
if (BareOutput || DisableCopyright)
|
||||
return;
|
||||
#if defined(__GNUC__) && defined(SFX_MODULE)
|
||||
mprintf(St(MCopyrightS));
|
||||
#else
|
||||
#ifndef SILENT
|
||||
static bool TitleShown=false;
|
||||
if (TitleShown)
|
||||
return;
|
||||
TitleShown=true;
|
||||
|
||||
wchar Version[80];
|
||||
if (RARVER_BETA!=0)
|
||||
swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
|
||||
else
|
||||
swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
|
||||
#if defined(_WIN_32) || defined(_WIN_64)
|
||||
wcsncatz(Version,L" ",ASIZE(Version));
|
||||
#endif
|
||||
#ifdef _WIN_32
|
||||
wcsncatz(Version,St(Mx86),ASIZE(Version));
|
||||
#endif
|
||||
#ifdef _WIN_64
|
||||
wcsncatz(Version,St(Mx64),ASIZE(Version));
|
||||
#endif
|
||||
if (PrintVersion)
|
||||
{
|
||||
mprintf(L"%s",Version);
|
||||
exit(0);
|
||||
}
|
||||
mprintf(St(MUCopyright),Version,RARVER_YEAR);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline bool CmpMSGID(MSGID i1,MSGID i2)
|
||||
{
|
||||
#ifdef MSGID_INT
|
||||
return i1==i2;
|
||||
#else
|
||||
// If MSGID is const char*, we cannot compare pointers only.
|
||||
// Pointers to different instances of same string can differ,
|
||||
// so we need to compare complete strings.
|
||||
return wcscmp(i1,i2)==0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommandData::OutHelp(RAR_EXIT ExitCode)
|
||||
{
|
||||
#if !defined(SILENT)
|
||||
OutTitle();
|
||||
static MSGID Help[]={
|
||||
#ifdef SFX_MODULE
|
||||
// Console SFX switches definition.
|
||||
MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
|
||||
#else
|
||||
// UnRAR switches definition.
|
||||
MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
|
||||
MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
|
||||
MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
|
||||
MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP,
|
||||
MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
|
||||
MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwMLP,
|
||||
MCHelpSwN,MCHelpSwNa,MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,
|
||||
MCHelpSwOM,MCHelpSwOP,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,MCHelpSwR,
|
||||
MCHelpSwRI,MCHelpSwSC,MCHelpSwSI,MCHelpSwSL,MCHelpSwTA,
|
||||
MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,
|
||||
MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY
|
||||
#endif
|
||||
};
|
||||
|
||||
for (uint I=0;I<ASIZE(Help);I++)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
if (CmpMSGID(Help[I],MCHelpSwV))
|
||||
continue;
|
||||
#ifndef _WIN_ALL
|
||||
static MSGID Win32Only[]={
|
||||
MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
|
||||
MCHelpSwEP2,MCHelpSwMLP,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
|
||||
};
|
||||
bool Found=false;
|
||||
for (uint J=0;J<ASIZE(Win32Only);J++)
|
||||
if (CmpMSGID(Help[I],Win32Only[J]))
|
||||
{
|
||||
Found=true;
|
||||
break;
|
||||
}
|
||||
if (Found)
|
||||
continue;
|
||||
#endif
|
||||
#ifdef _UNIX
|
||||
if (CmpMSGID(Help[I],MRARTitle2))
|
||||
{
|
||||
mprintf(St(MFwrSlTitle2));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#if !defined(_UNIX) && !defined(_WIN_ALL)
|
||||
if (CmpMSGID(Help[I],MCHelpSwOW))
|
||||
continue;
|
||||
#endif
|
||||
#ifndef _WIN_ALL
|
||||
if (CmpMSGID(Help[I],MCHelpSwAC))
|
||||
continue;
|
||||
#endif
|
||||
#ifndef SAVE_LINKS
|
||||
if (CmpMSGID(Help[I],MCHelpSwOL))
|
||||
continue;
|
||||
#endif
|
||||
#ifndef RAR_SMP
|
||||
if (CmpMSGID(Help[I],MCHelpSwMT))
|
||||
continue;
|
||||
#endif
|
||||
#endif
|
||||
mprintf(St(Help[I]));
|
||||
}
|
||||
mprintf(L"\n");
|
||||
ErrHandler.Exit(ExitCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
inline byte RangeCoder::GetChar()
|
||||
{
|
||||
return UnpackRead->GetChar();
|
||||
}
|
||||
|
||||
|
||||
void RangeCoder::InitDecoder(Unpack *UnpackRead)
|
||||
{
|
||||
RangeCoder::UnpackRead=UnpackRead;
|
||||
|
||||
low=code=0;
|
||||
range=0xffffffff;
|
||||
for (uint i = 0; i < 4; i++)
|
||||
code=(code << 8) | GetChar();
|
||||
}
|
||||
|
||||
|
||||
// (int) cast before "low" added only to suppress compiler warnings.
|
||||
#define ARI_DEC_NORMALIZE(code,low,range,read) \
|
||||
{ \
|
||||
while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \
|
||||
{ \
|
||||
code=(code << 8) | read->GetChar(); \
|
||||
range <<= 8; \
|
||||
low <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
inline int RangeCoder::GetCurrentCount()
|
||||
{
|
||||
return (code-low)/(range /= SubRange.scale);
|
||||
}
|
||||
|
||||
|
||||
inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT)
|
||||
{
|
||||
return (code-low)/(range >>= SHIFT);
|
||||
}
|
||||
|
||||
|
||||
inline void RangeCoder::Decode()
|
||||
{
|
||||
low += range*SubRange.LowCount;
|
||||
range *= SubRange.HighCount-SubRange.LowCount;
|
||||
}
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
/****************************************************************************
|
||||
* Contents: 'Carryless rangecoder' by Dmitry Subbotin *
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class RangeCoder
|
||||
{
|
||||
public:
|
||||
void InitDecoder(Unpack *UnpackRead);
|
||||
inline int GetCurrentCount();
|
||||
inline uint GetCurrentShiftCount(uint SHIFT);
|
||||
inline void Decode();
|
||||
inline void PutChar(unsigned int c);
|
||||
inline byte GetChar();
|
||||
|
||||
uint low, code, range;
|
||||
struct SUBRANGE
|
||||
{
|
||||
uint LowCount, HighCount, scale;
|
||||
} SubRange;
|
||||
|
||||
Unpack *UnpackRead;
|
||||
};
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
#ifndef _RAR_COMPRESS_
|
||||
#define _RAR_COMPRESS_
|
||||
|
||||
// Combine pack and unpack constants to class to avoid polluting global
|
||||
// namespace with numerous short names.
|
||||
class PackDef
|
||||
{
|
||||
public:
|
||||
// Maximum LZ match length we can encode even for short distances.
|
||||
static const uint MAX_LZ_MATCH = 0x1001;
|
||||
|
||||
// We increment LZ match length for longer distances, because shortest
|
||||
// matches are not allowed for them. Maximum length increment is 3
|
||||
// for distances larger than 256KB (0x40000). Here we define the maximum
|
||||
// incremented LZ match. Normally packer does not use it, but we must be
|
||||
// ready to process it in corrupt archives.
|
||||
static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3;
|
||||
|
||||
static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
|
||||
static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3;
|
||||
static const uint LOW_DIST_REP_COUNT = 16;
|
||||
|
||||
static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DCB = 64; // Base distance codes up to 4 GB.
|
||||
static const uint DCX = 80; // Extended distance codes up to 1 TB.
|
||||
static const uint LDC = 16;
|
||||
static const uint RC = 44;
|
||||
static const uint HUFF_TABLE_SIZEB = NC + DCB + RC + LDC;
|
||||
static const uint HUFF_TABLE_SIZEX = NC + DCX + RC + LDC;
|
||||
static const uint BC = 20;
|
||||
|
||||
static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC30 = 60;
|
||||
static const uint LDC30 = 17;
|
||||
static const uint RC30 = 28;
|
||||
static const uint BC30 = 20;
|
||||
static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30;
|
||||
|
||||
static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
|
||||
static const uint DC20 = 48;
|
||||
static const uint RC20 = 28;
|
||||
static const uint BC20 = 19;
|
||||
static const uint MC20 = 257;
|
||||
|
||||
// Largest alphabet size among all values listed above.
|
||||
static const uint LARGEST_TABLE_SIZE = 306;
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum FilterType {
|
||||
// These values must not be changed, because we use them directly
|
||||
// in RAR5 compression and decompression code.
|
||||
FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
|
||||
FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_TEXT,
|
||||
|
||||
// These values can be changed.
|
||||
FILTER_LONGRANGE,FILTER_EXHAUSTIVE,FILTER_NONE
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+416
@@ -0,0 +1,416 @@
|
||||
#include "rar.hpp"
|
||||
#include "log.cpp"
|
||||
|
||||
static MESSAGE_TYPE MsgStream=MSG_STDOUT;
|
||||
static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
|
||||
static bool ProhibitInput=false;
|
||||
static bool ConsoleOutputPresent=false;
|
||||
|
||||
static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
static bool IsRedirected(DWORD nStdHandle)
|
||||
{
|
||||
HANDLE hStd=GetStdHandle(nStdHandle);
|
||||
DWORD Mode;
|
||||
return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void InitConsole()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We want messages like file names or progress percent to be printed
|
||||
// immediately. Use only in Windows, in Unix they can cause wprintf %ls
|
||||
// to fail with non-English strings.
|
||||
setbuf(stdout,NULL);
|
||||
setbuf(stderr,NULL);
|
||||
|
||||
// Detect if output is redirected and set output mode properly.
|
||||
// We do not want to send Unicode output to files and especially to pipes
|
||||
// like '|more', which cannot handle them correctly in Windows.
|
||||
// In Unix console output is UTF-8 and it is handled correctly
|
||||
// when redirecting, so no need to perform any adjustments.
|
||||
StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
|
||||
StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
|
||||
StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
|
||||
#ifdef _MSC_VER
|
||||
if (!StdoutRedirected)
|
||||
_setmode(_fileno(stdout), _O_U16TEXT);
|
||||
if (!StderrRedirected)
|
||||
_setmode(_fileno(stderr), _O_U16TEXT);
|
||||
#endif
|
||||
#elif defined(_UNIX)
|
||||
StdoutRedirected=!isatty(fileno(stdout));
|
||||
StderrRedirected=!isatty(fileno(stderr));
|
||||
StdinRedirected=!isatty(fileno(stdin));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream)
|
||||
{
|
||||
::MsgStream=MsgStream;
|
||||
}
|
||||
|
||||
|
||||
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)
|
||||
{
|
||||
::RedirectCharset=RedirectCharset;
|
||||
}
|
||||
|
||||
|
||||
void ProhibitConsoleInput()
|
||||
{
|
||||
ProhibitInput=true;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
|
||||
{
|
||||
ConsoleOutputPresent=true;
|
||||
|
||||
// No need for PrintfPrepareFmt here, vwstrprintf calls it.
|
||||
std::wstring s=vwstrprintf(fmt,arglist);
|
||||
|
||||
ReplaceEsc(s);
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
|
||||
{
|
||||
HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
|
||||
DWORD Written;
|
||||
if (RedirectCharset==RCH_UNICODE)
|
||||
WriteFile(hOut,s.data(),(DWORD)s.size()*sizeof(s[0]),&Written,NULL);
|
||||
else
|
||||
{
|
||||
// Avoid Unicode for redirect in Windows, it does not work with pipes.
|
||||
std::string MsgA;
|
||||
if (RedirectCharset==RCH_UTF8)
|
||||
WideToUtf(s,MsgA);
|
||||
else
|
||||
WideToChar(s,MsgA);
|
||||
if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
|
||||
CharToOemA(&MsgA[0],&MsgA[0]); // Console tools like 'more' expect OEM encoding.
|
||||
|
||||
// We already converted \n to \r\n above, so we use WriteFile instead
|
||||
// of C library to avoid unnecessary additional conversion.
|
||||
WriteFile(hOut,MsgA.data(),(DWORD)MsgA.size(),&Written,NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// MSVC2008 vfwprintf writes every character to console separately
|
||||
// and it is too slow. We use direct WriteConsole call instead.
|
||||
HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
|
||||
DWORD Written;
|
||||
WriteConsole(hOut,s.data(),(DWORD)s.size(),&Written,NULL);
|
||||
#else
|
||||
fputws(s.c_str(),dest);
|
||||
// We do not use setbuf(NULL) in Unix (see comments in InitConsole).
|
||||
fflush(dest);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void mprintf(const wchar *fmt,...)
|
||||
{
|
||||
if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
|
||||
return;
|
||||
|
||||
fflush(stderr); // Ensure proper message order.
|
||||
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
|
||||
cvt_wprintf(dest,fmt,arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
void eprintf(const wchar *fmt,...)
|
||||
{
|
||||
if (MsgStream==MSG_NULL)
|
||||
return;
|
||||
|
||||
fflush(stdout); // Ensure proper message order.
|
||||
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
cvt_wprintf(stderr,fmt,arglist);
|
||||
va_end(arglist);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
static void QuitIfInputProhibited()
|
||||
{
|
||||
// We cannot handle user prompts if -si is used to read file or archive data
|
||||
// from stdin.
|
||||
if (ProhibitInput)
|
||||
{
|
||||
mprintf(St(MStdinNoInput));
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void GetPasswordText(std::wstring &Str)
|
||||
{
|
||||
QuitIfInputProhibited();
|
||||
if (StdinRedirected)
|
||||
getwstr(Str); // Read from pipe or redirected file.
|
||||
else
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD ConInMode;
|
||||
GetConsoleMode(hConIn,&ConInMode);
|
||||
SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT.
|
||||
|
||||
std::vector<wchar> Buf(MAXPASSWORD);
|
||||
|
||||
// We prefer ReadConsole to ReadFile, so we can read Unicode input.
|
||||
DWORD Read=0;
|
||||
ReadConsole(hConIn,Buf.data(),(DWORD)Buf.size()-1,&Read,NULL);
|
||||
Buf[Read]=0;
|
||||
Str=Buf.data();
|
||||
cleandata(Buf.data(),Buf.size()*sizeof(Buf[0]));
|
||||
|
||||
SetConsoleMode(hConIn,ConInMode);
|
||||
|
||||
// 2023.03.12: Previously we checked for presence of "\n" in entered
|
||||
// passwords, supposing that truncated strings do not include it.
|
||||
// We did it to read the rest of excessively long string, so it is not
|
||||
// read later as the second password for -p switch. But this "\n" check
|
||||
// doesn't seem to work in Windows 10 anymore and "\r" is present even
|
||||
// in truncated strings. Also we increased MAXPASSWORD, so it is larger
|
||||
// than MAXPASSWORD_RAR. Thus we removed this check as not working
|
||||
// and not that necessary. Low level FlushConsoleInputBuffer doesn't help
|
||||
// for high level ReadConsole, which in line input mode seems to store
|
||||
// the rest of string in its own internal buffer.
|
||||
#else
|
||||
std::vector<char> StrA(MAXPASSWORD*4); // "*4" for multibyte UTF-8 characters.
|
||||
#ifdef __VMS
|
||||
fgets(StrA.data(),StrA.size()-1,stdin);
|
||||
#elif defined(__sun)
|
||||
strncpyz(StrA.data(),getpassphrase(""),StrA.size());
|
||||
#else
|
||||
strncpyz(StrA.data(),getpass(""),StrA.size());
|
||||
#endif
|
||||
CharToWide(StrA.data(),Str);
|
||||
cleandata(StrA.data(),StrA.size()*sizeof(StrA[0]));
|
||||
#endif
|
||||
}
|
||||
RemoveLF(Str);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password)
|
||||
{
|
||||
if (!StdinRedirected)
|
||||
uiAlarm(UIALARM_QUESTION);
|
||||
|
||||
while (true)
|
||||
{
|
||||
// if (!StdinRedirected)
|
||||
if (Type==UIPASSWORD_GLOBAL)
|
||||
eprintf(L"\n%s: ",St(MAskPsw));
|
||||
else
|
||||
eprintf(St(MAskPswFor),FileName.c_str());
|
||||
|
||||
std::wstring PlainPsw;
|
||||
GetPasswordText(PlainPsw);
|
||||
if (PlainPsw.empty() && Type==UIPASSWORD_GLOBAL)
|
||||
return false;
|
||||
if (PlainPsw.size()>=MAXPASSWORD)
|
||||
{
|
||||
PlainPsw.erase(MAXPASSWORD-1);
|
||||
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1);
|
||||
}
|
||||
if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
|
||||
{
|
||||
eprintf(St(MReAskPsw));
|
||||
std::wstring CmpStr;
|
||||
GetPasswordText(CmpStr);
|
||||
if (CmpStr.empty() || PlainPsw!=CmpStr)
|
||||
{
|
||||
eprintf(St(MNotMatchPsw));
|
||||
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
|
||||
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
|
||||
continue;
|
||||
}
|
||||
cleandata(&CmpStr[0],CmpStr.size()*sizeof(CmpStr[0]));
|
||||
}
|
||||
Password->Set(PlainPsw.c_str());
|
||||
cleandata(&PlainPsw[0],PlainPsw.size()*sizeof(PlainPsw[0]));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
void getwstr(std::wstring &str)
|
||||
{
|
||||
// Print buffered prompt title function before waiting for input.
|
||||
fflush(stderr);
|
||||
|
||||
QuitIfInputProhibited();
|
||||
|
||||
str.clear();
|
||||
|
||||
const size_t MaxRead=MAXPATHSIZE; // Large enough to read a file name.
|
||||
|
||||
#if defined(_WIN_ALL)
|
||||
// fgetws does not work well with non-English text in Windows,
|
||||
// so we do not use it.
|
||||
if (StdinRedirected) // ReadConsole does not work if redirected.
|
||||
{
|
||||
// fgets does not work well with pipes in Windows in our test.
|
||||
// Let's use files.
|
||||
std::vector<char> StrA(MaxRead*4); // Up to 4 UTF-8 characters per wchar_t.
|
||||
File SrcFile;
|
||||
SrcFile.SetHandleType(FILE_HANDLESTD);
|
||||
SrcFile.SetLineInputMode(true);
|
||||
int ReadSize=SrcFile.Read(&StrA[0],StrA.size()-1);
|
||||
if (ReadSize<=0)
|
||||
{
|
||||
// Looks like stdin is a null device. We can enter to infinite loop
|
||||
// calling Ask() or set an empty password, so let's better exit.
|
||||
ErrHandler.ReadError(L"stdin");
|
||||
}
|
||||
StrA[ReadSize]=0;
|
||||
|
||||
// We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR,
|
||||
// such as send passwords, we get OEM encoding by default, unless we
|
||||
// use "chcp" in console. But we avoid OEM to ANSI conversion,
|
||||
// because we also want to handle ANSI files redirection correctly,
|
||||
// like "rar ... < ansifile.txt".
|
||||
CharToWide(&StrA[0],str);
|
||||
cleandata(&StrA[0],StrA.size()); // We can use this function to enter passwords.
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
|
||||
DWORD SizeToRead=(DWORD)Buf.size()-1;
|
||||
|
||||
// ReadConsole fails in Windows 7 for requested input exceeding 30 KB.
|
||||
// Not certain about Windows 8, so check for Windows 10 here.
|
||||
if (WinNT()<=WNT_W10)
|
||||
SizeToRead=Min(SizeToRead,0x4000);
|
||||
|
||||
DWORD ReadSize=0;
|
||||
if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),&Buf[0],SizeToRead,&ReadSize,NULL)==0)
|
||||
ErrHandler.ReadError(L"stdin"); // Unknown user input, safer to abort.
|
||||
Buf[ReadSize]=0;
|
||||
str=Buf.data();
|
||||
}
|
||||
#else
|
||||
std::vector<wchar> Buf(MaxRead); // Up to 4 UTF-8 characters per wchar_t.
|
||||
if (fgetws(&Buf[0],Buf.size(),stdin)==NULL)
|
||||
ErrHandler.ReadError(L"stdin"); // Avoid infinite Ask() loop.
|
||||
str=Buf.data();
|
||||
#endif
|
||||
RemoveLF(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SILENT
|
||||
// We allow this function to return 0 in case of invalid input,
|
||||
// because it might be convenient to press Enter to some not dangerous
|
||||
// prompts like "insert disk with next volume". We should call this function
|
||||
// again in case of 0 in dangerous prompt such as overwriting file.
|
||||
int Ask(const wchar *AskStr)
|
||||
{
|
||||
uiAlarm(UIALARM_QUESTION);
|
||||
|
||||
const uint MaxItems=10;
|
||||
wchar Item[MaxItems][40];
|
||||
uint ItemKeyPos[MaxItems],NumItems=0;
|
||||
|
||||
for (const wchar *NextItem=AskStr;NextItem!=nullptr;NextItem=wcschr(NextItem+1,'_'))
|
||||
{
|
||||
wchar *CurItem=Item[NumItems];
|
||||
wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
|
||||
wchar *EndItem=wcschr(CurItem,'_');
|
||||
if (EndItem!=nullptr)
|
||||
*EndItem=0;
|
||||
uint KeyPos=0,CurKey;
|
||||
while ((CurKey=CurItem[KeyPos])!=0)
|
||||
{
|
||||
bool Found=false;
|
||||
for (uint I=0;I<NumItems && !Found;I++)
|
||||
if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
|
||||
Found=true;
|
||||
if (!Found && CurKey!=' ')
|
||||
break;
|
||||
KeyPos++;
|
||||
}
|
||||
ItemKeyPos[NumItems]=KeyPos;
|
||||
NumItems++;
|
||||
}
|
||||
|
||||
for (uint I=0;I<NumItems;I++)
|
||||
{
|
||||
eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", ");
|
||||
uint KeyPos=ItemKeyPos[I];
|
||||
for (uint J=0;J<KeyPos;J++)
|
||||
eprintf(L"%c",Item[I][J]);
|
||||
eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
|
||||
}
|
||||
eprintf(L" ");
|
||||
std::wstring Str;
|
||||
getwstr(Str);
|
||||
wchar Ch=toupperw(Str[0]);
|
||||
for (uint I=0;I<NumItems;I++)
|
||||
if (Ch==Item[I][ItemKeyPos[I]])
|
||||
return I+1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static bool IsCommentUnsafe(const std::wstring &Data)
|
||||
{
|
||||
for (size_t I=0;I<Data.size();I++)
|
||||
if (Data[I]==27 && Data[I+1]=='[')
|
||||
for (size_t J=I+2;J<Data.size();J++)
|
||||
{
|
||||
// Return true for <ESC>[{key};"{string}"p used to redefine
|
||||
// a keyboard key on some terminals.
|
||||
if (Data[J]=='\"')
|
||||
return true;
|
||||
if (!IsDigit(Data[J]) && Data[J]!=';')
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void OutComment(const std::wstring &Comment)
|
||||
{
|
||||
if (IsCommentUnsafe(Comment))
|
||||
return;
|
||||
const size_t MaxOutSize=0x400;
|
||||
for (size_t I=0;I<Comment.size();I+=MaxOutSize)
|
||||
{
|
||||
size_t CopySize=Min(MaxOutSize,Comment.size()-I);
|
||||
mprintf(L"%s",Comment.substr(I,CopySize).c_str());
|
||||
}
|
||||
mprintf(L"\n");
|
||||
}
|
||||
|
||||
|
||||
bool IsConsoleOutputPresent()
|
||||
{
|
||||
return ConsoleOutputPresent;
|
||||
}
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
#ifndef _RAR_CONSIO_
|
||||
#define _RAR_CONSIO_
|
||||
|
||||
void InitConsole();
|
||||
void SetConsoleMsgStream(MESSAGE_TYPE MsgStream);
|
||||
void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset);
|
||||
void ProhibitConsoleInput();
|
||||
void OutComment(const std::wstring &Comment);
|
||||
bool IsConsoleOutputPresent();
|
||||
|
||||
#ifndef SILENT
|
||||
bool GetConsolePassword(UIPASSWORD_TYPE Type,const std::wstring &FileName,SecPassword *Password);
|
||||
#endif
|
||||
|
||||
#ifdef SILENT
|
||||
inline void mprintf(const wchar *fmt,...) {}
|
||||
inline void eprintf(const wchar *fmt,...) {}
|
||||
inline void Alarm() {}
|
||||
inline int Ask(const wchar *AskStr) {return 0;}
|
||||
inline void getwstr(std::wstring &str) {}
|
||||
#else
|
||||
void mprintf(const wchar *fmt,...);
|
||||
void eprintf(const wchar *fmt,...);
|
||||
void Alarm();
|
||||
int Ask(const wchar *AskStr);
|
||||
void getwstr(std::wstring &str);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+280
@@ -0,0 +1,280 @@
|
||||
// This CRC function is based on Intel Slicing-by-8 algorithm.
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is available here:
|
||||
//
|
||||
// http://sourceforge.net/projects/slicing-by-8/
|
||||
//
|
||||
// Original Intel Slicing-by-8 code is licensed as:
|
||||
//
|
||||
// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
|
||||
//
|
||||
// This software program is licensed subject to the BSD License,
|
||||
// available at http://www.opensource.org/licenses/bsd-license.html
|
||||
|
||||
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// User suggested to avoid BSD license in SFX module, so they do not need
|
||||
// to include the license to SFX archive.
|
||||
#define USE_SLICING
|
||||
#endif
|
||||
|
||||
static uint crc_tables[16][256]; // Tables for Slicing-by-16.
|
||||
|
||||
#ifdef USE_NEON_CRC32
|
||||
static bool CRC_Neon;
|
||||
#endif
|
||||
|
||||
|
||||
// Build the classic CRC32 lookup table.
|
||||
// We also provide this function to legacy RAR and ZIP decryption code.
|
||||
void InitCRC32(uint *CRCTab)
|
||||
{
|
||||
if (CRCTab[1]!=0)
|
||||
return;
|
||||
for (uint I=0;I<256;I++)
|
||||
{
|
||||
uint C=I;
|
||||
for (uint J=0;J<8;J++)
|
||||
C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
|
||||
CRCTab[I]=C;
|
||||
}
|
||||
|
||||
#ifdef USE_NEON_CRC32
|
||||
#ifdef _APPLE
|
||||
// getauxval isn't available in OS X
|
||||
uint Value=0;
|
||||
size_t Size=sizeof(Value);
|
||||
int RetCode=sysctlbyname("hw.optional.armv8_crc32",&Value,&Size,NULL,0);
|
||||
CRC_Neon=RetCode==0 && Value!=0;
|
||||
#else
|
||||
CRC_Neon=(getauxval(AT_HWCAP) & HWCAP_CRC32)!=0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void InitTables()
|
||||
{
|
||||
InitCRC32(crc_tables[0]);
|
||||
|
||||
#ifdef USE_SLICING
|
||||
for (uint I=0;I<256;I++) // Build additional lookup tables.
|
||||
{
|
||||
uint C=crc_tables[0][I];
|
||||
for (uint J=1;J<16;J++)
|
||||
{
|
||||
C=crc_tables[0][(byte)C]^(C>>8);
|
||||
crc_tables[J][I]=C;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
|
||||
|
||||
uint CRC32(uint StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
|
||||
#ifdef USE_NEON_CRC32
|
||||
if (CRC_Neon)
|
||||
{
|
||||
for (;Size>=8;Size-=8,Data+=8)
|
||||
#ifdef __clang__
|
||||
StartCRC = __builtin_arm_crc32d(StartCRC, RawGet8(Data));
|
||||
#else
|
||||
StartCRC = __builtin_aarch64_crc32x(StartCRC, RawGet8(Data));
|
||||
#endif
|
||||
for (;Size>0;Size--,Data++) // Process left data.
|
||||
#ifdef __clang__
|
||||
StartCRC = __builtin_arm_crc32b(StartCRC, *Data);
|
||||
#else
|
||||
StartCRC = __builtin_aarch64_crc32b(StartCRC, *Data);
|
||||
#endif
|
||||
return StartCRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SLICING
|
||||
// Align Data to 16 for better performance and to avoid ALLOW_MISALIGNED
|
||||
// check below.
|
||||
for (;Size>0 && ((size_t)Data & 15)!=0;Size--,Data++)
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
// 2023.12.06: We switched to slicing-by-16, which seems to be faster than
|
||||
// slicing-by-8 on modern CPUs. Slicing-by-32 would require 32 KB for tables
|
||||
// and could be limited by L1 cache size on some CPUs.
|
||||
for (;Size>=16;Size-=16,Data+=16)
|
||||
{
|
||||
#ifdef BIG_ENDIAN
|
||||
StartCRC ^= RawGet4(Data);
|
||||
uint D1 = RawGet4(Data+4);
|
||||
uint D2 = RawGet4(Data+8);
|
||||
uint D3 = RawGet4(Data+12);
|
||||
#else
|
||||
// We avoid RawGet4 here for performance reason, to access uint32
|
||||
// directly even if ALLOW_MISALIGNED isn't defined. We can do it,
|
||||
// because we aligned 'Data' above.
|
||||
StartCRC ^= *(uint32 *) Data;
|
||||
uint D1 = *(uint32 *) (Data+4);
|
||||
uint D2 = *(uint32 *) (Data+8);
|
||||
uint D3 = *(uint32 *) (Data+12);
|
||||
#endif
|
||||
StartCRC = crc_tables[15][(byte) StartCRC ] ^
|
||||
crc_tables[14][(byte)(StartCRC >> 8) ] ^
|
||||
crc_tables[13][(byte)(StartCRC >> 16)] ^
|
||||
crc_tables[12][(byte)(StartCRC >> 24)] ^
|
||||
crc_tables[11][(byte) D1 ] ^
|
||||
crc_tables[10][(byte)(D1 >> 8) ] ^
|
||||
crc_tables[ 9][(byte)(D1 >> 16)] ^
|
||||
crc_tables[ 8][(byte)(D1 >> 24)] ^
|
||||
crc_tables[ 7][(byte) D2 ] ^
|
||||
crc_tables[ 6][(byte)(D2 >> 8)] ^
|
||||
crc_tables[ 5][(byte)(D2 >> 16)] ^
|
||||
crc_tables[ 4][(byte)(D2 >> 24)] ^
|
||||
crc_tables[ 3][(byte) D3 ] ^
|
||||
crc_tables[ 2][(byte)(D3 >> 8)] ^
|
||||
crc_tables[ 1][(byte)(D3 >> 16)] ^
|
||||
crc_tables[ 0][(byte)(D3 >> 24)];
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;Size>0;Size--,Data++) // Process left data.
|
||||
StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
|
||||
|
||||
return StartCRC;
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
// For RAR 1.4 archives in case somebody still has them.
|
||||
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
|
||||
{
|
||||
byte *Data=(byte *)Addr;
|
||||
for (size_t I=0;I<Size;I++)
|
||||
{
|
||||
StartCRC=(StartCRC+Data[I])&0xffff;
|
||||
StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff;
|
||||
}
|
||||
return StartCRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
static void TestCRC();
|
||||
struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC;
|
||||
|
||||
void TestCRC()
|
||||
{
|
||||
// This function is invoked from global object and _SSE_Version is global
|
||||
// and can be initialized after this function. So we explicitly initialize
|
||||
// it here to enable SSE support in Blake2sp.
|
||||
_SSE_Version=GetSSEVersion();
|
||||
|
||||
const uint FirstSize=300;
|
||||
byte b[FirstSize];
|
||||
|
||||
if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84)
|
||||
mprintf(L"\nCRC32 test1 OK");
|
||||
else
|
||||
mprintf(L"\nCRC32 test1 FAILED");
|
||||
|
||||
if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE)
|
||||
mprintf(L"\nCRC32 test2 OK");
|
||||
else
|
||||
mprintf(L"\nCRC32 test2 FAILED");
|
||||
|
||||
for (uint I=0;I<14;I++) // Check for possible int sign extension.
|
||||
b[I]=(byte)0x7f+I;
|
||||
if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA)
|
||||
mprintf(L"\nCRC32 test3 OK");
|
||||
else
|
||||
mprintf(L"\nCRC32 test3 FAILED");
|
||||
|
||||
for (uint I=0;I<FirstSize;I++)
|
||||
b[I]=(byte)I;
|
||||
uint r32=CRC32(0xffffffff,b,FirstSize);
|
||||
for (uint I=FirstSize;I<1024;I++)
|
||||
{
|
||||
b[0]=(byte)I;
|
||||
r32=CRC32(r32,b,1);
|
||||
}
|
||||
if ((r32^0xffffffff)==0xB70B4C26)
|
||||
mprintf(L"\nCRC32 test4 OK");
|
||||
else
|
||||
mprintf(L"\nCRC32 test4 FAILED");
|
||||
|
||||
if ((CRC64(0xffffffffffffffff,(byte*)"testtesttest",12)^0xffffffffffffffff)==0x7B1C2D230EDEB436)
|
||||
mprintf(L"\nCRC64 test1 OK");
|
||||
else
|
||||
mprintf(L"\nCRC64 test1 FAILED");
|
||||
|
||||
if (CRC64(0,(byte*)"te\x80st",5)==0xB5DBF9583A6EED4A)
|
||||
mprintf(L"\nCRC64 test2 OK");
|
||||
else
|
||||
mprintf(L"\nCRC64 test2 FAILED");
|
||||
|
||||
for (uint I=0;I<14;I++) // Check for possible int sign extension.
|
||||
b[I]=(byte)0x7f+I;
|
||||
if ((CRC64(0xffffffffffffffff,b,14)^0xffffffffffffffff)==0xE019941C05B2820C)
|
||||
mprintf(L"\nCRC64 test3 OK");
|
||||
else
|
||||
mprintf(L"\nCRC64 test3 FAILED");
|
||||
|
||||
for (uint I=0;I<FirstSize;I++)
|
||||
b[I]=(byte)I;
|
||||
uint64 r64=CRC64(0xffffffffffffffff,b,FirstSize);
|
||||
for (uint I=FirstSize;I<1024;I++)
|
||||
{
|
||||
b[0]=(byte)I;
|
||||
r64=CRC64(r64,b,1);
|
||||
}
|
||||
if ((r64^0xffffffffffffffff)==0xD51FB58DC789C400)
|
||||
mprintf(L"\nCRC64 test4 OK");
|
||||
else
|
||||
mprintf(L"\nCRC64 test4 FAILED");
|
||||
|
||||
const size_t BufSize=0x100000;
|
||||
byte *Buf=new byte[BufSize];
|
||||
GetRnd(Buf,BufSize);
|
||||
|
||||
clock_t StartTime=clock();
|
||||
r32=0xffffffff;
|
||||
const uint64 BufCount=5000;
|
||||
for (uint I=0;I<BufCount;I++)
|
||||
r32=CRC32(r32,Buf,BufSize);
|
||||
if (r32!=0) // Otherwise compiler optimizer removes CRC calculation.
|
||||
mprintf(L"\nCRC32 speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
|
||||
|
||||
StartTime=clock();
|
||||
DataHash Hash;
|
||||
Hash.Init(HASH_CRC32,MaxPoolThreads);
|
||||
const uint64 BufCountMT=20000;
|
||||
for (uint I=0;I<BufCountMT;I++)
|
||||
Hash.Update(Buf,BufSize);
|
||||
HashValue Result;
|
||||
Hash.Result(&Result);
|
||||
mprintf(L"\nCRC32 MT speed: %llu MB/s",BufCountMT*CLOCKS_PER_SEC/(clock()-StartTime));
|
||||
|
||||
StartTime=clock();
|
||||
Hash.Init(HASH_BLAKE2,MaxPoolThreads);
|
||||
for (uint I=0;I<BufCount;I++)
|
||||
Hash.Update(Buf,BufSize);
|
||||
Hash.Result(&Result);
|
||||
mprintf(L"\nBlake2sp speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
|
||||
|
||||
StartTime=clock();
|
||||
r64=0xffffffffffffffff;
|
||||
for (uint I=0;I<BufCount;I++)
|
||||
r64=CRC64(r64,Buf,BufSize);
|
||||
if (r64!=0) // Otherwise compiler optimizer removes CRC calculation.
|
||||
mprintf(L"\nCRC64 speed: %llu MB/s",BufCount*CLOCKS_PER_SEC/(clock()-StartTime));
|
||||
}
|
||||
#endif
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
#ifndef _RAR_CRC_
|
||||
#define _RAR_CRC_
|
||||
|
||||
// This function is only to intialize external CRC tables. We do not need to
|
||||
// call it before calculating CRC32.
|
||||
void InitCRC32(uint *CRCTab);
|
||||
|
||||
uint CRC32(uint StartCRC,const void *Addr,size_t Size);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
#include "crypt1.cpp"
|
||||
#include "crypt2.cpp"
|
||||
#endif
|
||||
#include "crypt3.cpp"
|
||||
#include "crypt5.cpp"
|
||||
|
||||
|
||||
CryptData::CryptData()
|
||||
{
|
||||
Method=CRYPT_NONE;
|
||||
KDF3CachePos=0;
|
||||
KDF5CachePos=0;
|
||||
memset(CRCTab,0,sizeof(CRCTab));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CryptData::DecryptBlock(byte *Buf,size_t Size)
|
||||
{
|
||||
switch(Method)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
case CRYPT_RAR13:
|
||||
Decrypt13(Buf,Size);
|
||||
break;
|
||||
case CRYPT_RAR15:
|
||||
Crypt15(Buf,Size);
|
||||
break;
|
||||
case CRYPT_RAR20:
|
||||
for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE)
|
||||
DecryptBlock20(Buf+I);
|
||||
break;
|
||||
#endif
|
||||
case CRYPT_RAR30:
|
||||
case CRYPT_RAR50:
|
||||
rin.blockDecrypt(Buf,Size,Buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
|
||||
SecPassword *Password,const byte *Salt,
|
||||
const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
|
||||
{
|
||||
if (Method==CRYPT_NONE || !Password->IsSet())
|
||||
return false;
|
||||
|
||||
CryptData::Method=Method;
|
||||
|
||||
wchar PwdW[MAXPASSWORD];
|
||||
Password->Get(PwdW,ASIZE(PwdW));
|
||||
|
||||
if (wcslen(PwdW)>=MAXPASSWORD_RAR)
|
||||
uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD_RAR-1);
|
||||
|
||||
PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
|
||||
|
||||
char PwdA[MAXPASSWORD];
|
||||
WideToChar(PwdW,PwdA,ASIZE(PwdA));
|
||||
PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives.
|
||||
|
||||
bool Success=true;
|
||||
|
||||
switch(Method)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
case CRYPT_RAR13:
|
||||
SetKey13(PwdA);
|
||||
break;
|
||||
case CRYPT_RAR15:
|
||||
SetKey15(PwdA);
|
||||
break;
|
||||
case CRYPT_RAR20:
|
||||
SetKey20(PwdA);
|
||||
break;
|
||||
#endif
|
||||
case CRYPT_RAR30:
|
||||
SetKey30(Encrypt,Password,PwdW,Salt);
|
||||
break;
|
||||
case CRYPT_RAR50:
|
||||
Success=SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
|
||||
break;
|
||||
}
|
||||
cleandata(PwdA,sizeof(PwdA));
|
||||
cleandata(PwdW,sizeof(PwdW));
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
// Use the current system time to additionally randomize data.
|
||||
static void TimeRandomize(byte *RndBuf,size_t BufSize)
|
||||
{
|
||||
static uint Count=0;
|
||||
RarTime CurTime;
|
||||
CurTime.SetCurrentTime();
|
||||
uint64 Random=CurTime.GetWin()+clock();
|
||||
for (size_t I=0;I<BufSize;I++)
|
||||
{
|
||||
byte RndByte = byte (Random >> ( (I & 7) * 8 ));
|
||||
RndBuf[I]=byte( (RndByte ^ I) + Count++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Fill buffer with random data.
|
||||
void GetRnd(byte *RndBuf,size_t BufSize)
|
||||
{
|
||||
bool Success=false;
|
||||
#if defined(_WIN_ALL)
|
||||
HCRYPTPROV hProvider = 0;
|
||||
if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
||||
{
|
||||
Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) != FALSE;
|
||||
CryptReleaseContext(hProvider, 0);
|
||||
}
|
||||
#elif defined(_UNIX)
|
||||
FILE *rndf = fopen("/dev/urandom", "r");
|
||||
if (rndf!=NULL)
|
||||
{
|
||||
Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
|
||||
fclose(rndf);
|
||||
}
|
||||
#endif
|
||||
// We use this code only as the last resort if code above failed.
|
||||
if (!Success)
|
||||
TimeRandomize(RndBuf,BufSize);
|
||||
}
|
||||
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
#ifndef _RAR_CRYPT_
|
||||
#define _RAR_CRYPT_
|
||||
|
||||
|
||||
enum CRYPT_METHOD {
|
||||
CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50,
|
||||
CRYPT_UNKNOWN
|
||||
};
|
||||
|
||||
#define SIZE_SALT50 16
|
||||
#define SIZE_SALT30 8
|
||||
#define SIZE_INITV 16
|
||||
#define SIZE_PSWCHECK 8
|
||||
#define SIZE_PSWCHECK_CSUM 4
|
||||
|
||||
#define CRYPT_BLOCK_SIZE 16
|
||||
#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
|
||||
|
||||
#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
|
||||
#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
|
||||
#define CRYPT_VERSION 0 // Supported encryption version.
|
||||
|
||||
|
||||
class CryptData
|
||||
{
|
||||
struct KDF5CacheItem
|
||||
{
|
||||
SecPassword Pwd;
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte Key[32];
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
byte PswCheckValue[SHA256_DIGEST_SIZE];
|
||||
byte HashKeyValue[SHA256_DIGEST_SIZE];
|
||||
|
||||
KDF5CacheItem() {Clean();}
|
||||
~KDF5CacheItem() {Clean();}
|
||||
|
||||
void Clean()
|
||||
{
|
||||
cleandata(Salt,sizeof(Salt));
|
||||
cleandata(Key,sizeof(Key));
|
||||
cleandata(&Lg2Count,sizeof(Lg2Count));
|
||||
cleandata(PswCheckValue,sizeof(PswCheckValue));
|
||||
cleandata(HashKeyValue,sizeof(HashKeyValue));
|
||||
}
|
||||
};
|
||||
|
||||
struct KDF3CacheItem
|
||||
{
|
||||
SecPassword Pwd;
|
||||
byte Salt[SIZE_SALT30];
|
||||
byte Key[16];
|
||||
byte Init[16];
|
||||
bool SaltPresent;
|
||||
|
||||
KDF3CacheItem() {Clean();}
|
||||
~KDF3CacheItem() {Clean();}
|
||||
|
||||
void Clean()
|
||||
{
|
||||
cleandata(Salt,sizeof(Salt));
|
||||
cleandata(Key,sizeof(Key));
|
||||
cleandata(Init,sizeof(Init));
|
||||
cleandata(&SaltPresent,sizeof(SaltPresent));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
void SetKey13(const char *Password);
|
||||
void Decrypt13(byte *Data,size_t Count);
|
||||
|
||||
void SetKey15(const char *Password);
|
||||
void Crypt15(byte *Data,size_t Count);
|
||||
|
||||
void SetKey20(const char *Password);
|
||||
void Swap20(byte *Ch1,byte *Ch2);
|
||||
void UpdKeys20(byte *Buf);
|
||||
void EncryptBlock20(byte *Buf);
|
||||
void DecryptBlock20(byte *Buf);
|
||||
|
||||
void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
|
||||
bool SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
|
||||
|
||||
KDF3CacheItem KDF3Cache[4];
|
||||
uint KDF3CachePos;
|
||||
|
||||
KDF5CacheItem KDF5Cache[4];
|
||||
uint KDF5CachePos;
|
||||
|
||||
CRYPT_METHOD Method;
|
||||
|
||||
Rijndael rin;
|
||||
|
||||
uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
|
||||
|
||||
byte SubstTable20[256];
|
||||
uint Key20[4];
|
||||
|
||||
byte Key13[3];
|
||||
ushort Key15[4];
|
||||
public:
|
||||
CryptData();
|
||||
bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
|
||||
const byte *Salt,const byte *InitV,uint Lg2Cnt,
|
||||
byte *HashKey,byte *PswCheck);
|
||||
void SetCmt13Encryption();
|
||||
void EncryptBlock(byte *Buf,size_t Size);
|
||||
void DecryptBlock(byte *Buf,size_t Size);
|
||||
static void SetSalt(byte *Salt,size_t SaltSize);
|
||||
};
|
||||
|
||||
|
||||
class CheckPassword
|
||||
{
|
||||
public:
|
||||
enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW};
|
||||
virtual CONFIDENCE GetConfidence()=0;
|
||||
virtual bool Check(SecPassword *Password)=0;
|
||||
};
|
||||
|
||||
class RarCheckPassword:public CheckPassword
|
||||
{
|
||||
private:
|
||||
CryptData *Crypt;
|
||||
uint Lg2Count;
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte InitV[SIZE_INITV];
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
public:
|
||||
RarCheckPassword()
|
||||
{
|
||||
Crypt=NULL;
|
||||
}
|
||||
~RarCheckPassword()
|
||||
{
|
||||
delete Crypt;
|
||||
}
|
||||
void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck)
|
||||
{
|
||||
if (Crypt==NULL)
|
||||
Crypt=new CryptData;
|
||||
memcpy(this->Salt,Salt,sizeof(this->Salt));
|
||||
memcpy(this->InitV,InitV,sizeof(this->InitV));
|
||||
this->Lg2Count=Lg2Count;
|
||||
memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck));
|
||||
}
|
||||
bool IsSet() {return Crypt!=NULL;}
|
||||
|
||||
// RAR5 provides the higly reliable 64 bit password verification value.
|
||||
CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;}
|
||||
|
||||
bool Check(SecPassword *Password)
|
||||
{
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck);
|
||||
return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0;
|
||||
}
|
||||
};
|
||||
|
||||
void GetRnd(byte *RndBuf,size_t BufSize);
|
||||
|
||||
void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
|
||||
size_t DataLength,byte *ResDigest);
|
||||
void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
|
||||
size_t salt_len,byte *key, byte *Value1, byte *Value2,
|
||||
uint rounds);
|
||||
|
||||
void ConvertHashToMAC(HashValue *Value,byte *Key);
|
||||
|
||||
#endif
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
void CryptData::SetKey13(const char *Password)
|
||||
{
|
||||
Key13[0]=Key13[1]=Key13[2]=0;
|
||||
for (size_t I=0;Password[I]!=0;I++)
|
||||
{
|
||||
byte P=Password[I];
|
||||
Key13[0]+=P;
|
||||
Key13[1]^=P;
|
||||
Key13[2]+=P;
|
||||
Key13[2]=(byte)rotls(Key13[2],1,8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetKey15(const char *Password)
|
||||
{
|
||||
InitCRC32(CRCTab);
|
||||
uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
|
||||
Key15[0]=PswCRC&0xffff;
|
||||
Key15[1]=(PswCRC>>16)&0xffff;
|
||||
Key15[2]=Key15[3]=0;
|
||||
for (size_t I=0;Password[I]!=0;I++)
|
||||
{
|
||||
byte P=Password[I];
|
||||
Key15[2]^=P^CRCTab[P];
|
||||
Key15[3]+=ushort(P+(CRCTab[P]>>16));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::SetCmt13Encryption()
|
||||
{
|
||||
Method=CRYPT_RAR13;
|
||||
Key13[0]=0;
|
||||
Key13[1]=7;
|
||||
Key13[2]=77;
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Decrypt13(byte *Data,size_t Count)
|
||||
{
|
||||
while (Count--)
|
||||
{
|
||||
Key13[1]+=Key13[2];
|
||||
Key13[0]+=Key13[1];
|
||||
*Data-=Key13[0];
|
||||
Data++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Crypt15(byte *Data,size_t Count)
|
||||
{
|
||||
while (Count--)
|
||||
{
|
||||
Key15[0]+=0x1234;
|
||||
Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
|
||||
Key15[2]-=ushort(CRCTab[(Key15[0] & 0x1fe)>>1]>>16);
|
||||
Key15[0]^=Key15[2];
|
||||
Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
|
||||
Key15[3]=rotrs(Key15[3]&0xffff,1,16);
|
||||
Key15[0]^=Key15[3];
|
||||
*Data^=(byte)(Key15[0]>>8);
|
||||
Data++;
|
||||
}
|
||||
}
|
||||
Vendored
+133
@@ -0,0 +1,133 @@
|
||||
#define NROUNDS 32
|
||||
|
||||
#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
|
||||
((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
|
||||
((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
|
||||
((uint)SubstTable20[(int)(t>>24)&255]<<24) )
|
||||
|
||||
|
||||
static byte InitSubstTable20[256]={
|
||||
215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
|
||||
232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
|
||||
255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
|
||||
71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
|
||||
107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
|
||||
158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
|
||||
97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
|
||||
164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
|
||||
207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
|
||||
122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
|
||||
131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
|
||||
224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
|
||||
118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
|
||||
161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
|
||||
0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
|
||||
116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
|
||||
};
|
||||
|
||||
|
||||
void CryptData::SetKey20(const char *Password)
|
||||
{
|
||||
InitCRC32(CRCTab);
|
||||
|
||||
char Psw[MAXPASSWORD];
|
||||
strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
|
||||
size_t PswLength=strlen(Psw);
|
||||
|
||||
Key20[0]=0xD3A3B879L;
|
||||
Key20[1]=0x3F6D12F7L;
|
||||
Key20[2]=0x7515A235L;
|
||||
Key20[3]=0xA4E7F123L;
|
||||
|
||||
memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
|
||||
for (uint J=0;J<256;J++)
|
||||
for (size_t I=0;I<PswLength;I+=2)
|
||||
{
|
||||
uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff];
|
||||
uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff];
|
||||
for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++)
|
||||
Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]);
|
||||
}
|
||||
|
||||
// Incomplete last block of password must be zero padded.
|
||||
if ((PswLength & CRYPT_BLOCK_MASK)!=0)
|
||||
for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++)
|
||||
Psw[I]=0;
|
||||
|
||||
for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE)
|
||||
EncryptBlock20((byte *)Psw+I);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::EncryptBlock20(byte *Buf)
|
||||
{
|
||||
uint A,B,C,D,T,TA,TB;
|
||||
A=RawGet4(Buf+0)^Key20[0];
|
||||
B=RawGet4(Buf+4)^Key20[1];
|
||||
C=RawGet4(Buf+8)^Key20[2];
|
||||
D=RawGet4(Buf+12)^Key20[3];
|
||||
for(int I=0;I<NROUNDS;I++)
|
||||
{
|
||||
T=((C+rotls(D,11,32))^Key20[I&3]);
|
||||
TA=A^substLong(T);
|
||||
T=((D^rotls(C,17,32))+Key20[I&3]);
|
||||
TB=B^substLong(T);
|
||||
A=C;
|
||||
B=D;
|
||||
C=TA;
|
||||
D=TB;
|
||||
}
|
||||
RawPut4(C^Key20[0],Buf+0);
|
||||
RawPut4(D^Key20[1],Buf+4);
|
||||
RawPut4(A^Key20[2],Buf+8);
|
||||
RawPut4(B^Key20[3],Buf+12);
|
||||
UpdKeys20(Buf);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::DecryptBlock20(byte *Buf)
|
||||
{
|
||||
byte InBuf[16];
|
||||
uint A,B,C,D,T,TA,TB;
|
||||
A=RawGet4(Buf+0)^Key20[0];
|
||||
B=RawGet4(Buf+4)^Key20[1];
|
||||
C=RawGet4(Buf+8)^Key20[2];
|
||||
D=RawGet4(Buf+12)^Key20[3];
|
||||
memcpy(InBuf,Buf,sizeof(InBuf));
|
||||
for(int I=NROUNDS-1;I>=0;I--)
|
||||
{
|
||||
T=((C+rotls(D,11,32))^Key20[I&3]);
|
||||
TA=A^substLong(T);
|
||||
T=((D^rotls(C,17,32))+Key20[I&3]);
|
||||
TB=B^substLong(T);
|
||||
A=C;
|
||||
B=D;
|
||||
C=TA;
|
||||
D=TB;
|
||||
}
|
||||
RawPut4(C^Key20[0],Buf+0);
|
||||
RawPut4(D^Key20[1],Buf+4);
|
||||
RawPut4(A^Key20[2],Buf+8);
|
||||
RawPut4(B^Key20[3],Buf+12);
|
||||
UpdKeys20(InBuf);
|
||||
}
|
||||
|
||||
|
||||
void CryptData::UpdKeys20(byte *Buf)
|
||||
{
|
||||
for (int I=0;I<16;I+=4)
|
||||
{
|
||||
Key20[0]^=CRCTab[Buf[I]];
|
||||
Key20[1]^=CRCTab[Buf[I+1]];
|
||||
Key20[2]^=CRCTab[Buf[I+2]];
|
||||
Key20[3]^=CRCTab[Buf[I+3]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CryptData::Swap20(byte *Ch1,byte *Ch2)
|
||||
{
|
||||
byte Ch=*Ch1;
|
||||
*Ch1=*Ch2;
|
||||
*Ch2=Ch;
|
||||
}
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
|
||||
{
|
||||
byte AESKey[16],AESInit[16];
|
||||
|
||||
bool Cached=false;
|
||||
for (uint I=0;I<ASIZE(KDF3Cache);I++)
|
||||
if (KDF3Cache[I].Pwd==*Password &&
|
||||
(Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL &&
|
||||
KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0))
|
||||
{
|
||||
memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey));
|
||||
SecHideData(AESKey,sizeof(AESKey),false,false);
|
||||
memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit));
|
||||
Cached=true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Cached)
|
||||
{
|
||||
byte RawPsw[2*MAXPASSWORD+SIZE_SALT30];
|
||||
size_t PswLength=wcslen(PwdW);
|
||||
size_t RawLength=2*PswLength;
|
||||
WideToRaw(PwdW,PswLength,RawPsw,RawLength);
|
||||
if (Salt!=NULL)
|
||||
{
|
||||
memcpy(RawPsw+RawLength,Salt,SIZE_SALT30);
|
||||
RawLength+=SIZE_SALT30;
|
||||
}
|
||||
sha1_context c;
|
||||
sha1_init(&c);
|
||||
|
||||
const uint HashRounds=0x40000;
|
||||
for (uint I=0;I<HashRounds;I++)
|
||||
{
|
||||
sha1_process_rar29( &c, RawPsw, RawLength );
|
||||
byte PswNum[3];
|
||||
PswNum[0]=(byte)I;
|
||||
PswNum[1]=(byte)(I>>8);
|
||||
PswNum[2]=(byte)(I>>16);
|
||||
sha1_process(&c, PswNum, 3);
|
||||
if (I%(HashRounds/16)==0)
|
||||
{
|
||||
sha1_context tempc=c;
|
||||
uint32 digest[5];
|
||||
sha1_done( &tempc, digest );
|
||||
AESInit[I/(HashRounds/16)]=(byte)digest[4];
|
||||
}
|
||||
}
|
||||
uint32 digest[5];
|
||||
sha1_done( &c, digest );
|
||||
for (uint I=0;I<4;I++)
|
||||
for (uint J=0;J<4;J++)
|
||||
AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
|
||||
|
||||
KDF3Cache[KDF3CachePos].Pwd=*Password;
|
||||
if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
|
||||
memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
|
||||
memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
|
||||
SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false);
|
||||
memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
|
||||
KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
|
||||
|
||||
cleandata(RawPsw,sizeof(RawPsw));
|
||||
}
|
||||
rin.Init(Encrypt, AESKey, 128, AESInit);
|
||||
cleandata(AESKey,sizeof(AESKey));
|
||||
cleandata(AESInit,sizeof(AESInit));
|
||||
}
|
||||
|
||||
Vendored
+235
@@ -0,0 +1,235 @@
|
||||
static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
|
||||
size_t DataLength,byte *ResDigest,
|
||||
sha256_context *ICtxOpt,bool *SetIOpt,
|
||||
sha256_context *RCtxOpt,bool *SetROpt)
|
||||
{
|
||||
const size_t Sha256BlockSize=64; // As defined in RFC 4868.
|
||||
|
||||
byte KeyHash[SHA256_DIGEST_SIZE];
|
||||
if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
|
||||
{
|
||||
sha256_context KCtx;
|
||||
sha256_init(&KCtx);
|
||||
sha256_process(&KCtx, Key, KeyLength);
|
||||
sha256_done(&KCtx, KeyHash);
|
||||
|
||||
Key = KeyHash;
|
||||
KeyLength = SHA256_DIGEST_SIZE;
|
||||
}
|
||||
|
||||
byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
|
||||
sha256_context ICtx;
|
||||
|
||||
if (ICtxOpt!=NULL && *SetIOpt)
|
||||
ICtx=*ICtxOpt; // Use already calculated the first block context.
|
||||
else
|
||||
{
|
||||
// This calculation is the same for all iterations with same password.
|
||||
// So for PBKDF2 we can calculate it only for first block and then reuse
|
||||
// to improve performance.
|
||||
|
||||
for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
|
||||
KeyBuf[I] = Key[I] ^ 0x36;
|
||||
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
|
||||
KeyBuf[I] = 0x36;
|
||||
|
||||
sha256_init(&ICtx);
|
||||
sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
|
||||
}
|
||||
|
||||
if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse.
|
||||
{
|
||||
*ICtxOpt=ICtx;
|
||||
*SetIOpt=true;
|
||||
}
|
||||
|
||||
sha256_process(&ICtx, Data, DataLength); // Hash data.
|
||||
|
||||
byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
|
||||
sha256_done(&ICtx, IDig);
|
||||
|
||||
sha256_context RCtx;
|
||||
|
||||
if (RCtxOpt!=NULL && *SetROpt)
|
||||
RCtx=*RCtxOpt; // Use already calculated first block context.
|
||||
else
|
||||
{
|
||||
// This calculation is the same for all iterations with same password.
|
||||
// So for PBKDF2 we can calculate it only for first block and then reuse
|
||||
// to improve performance.
|
||||
|
||||
for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
|
||||
KeyBuf[I] = Key[I] ^ 0x5c;
|
||||
for (size_t I = KeyLength; I < Sha256BlockSize; I++)
|
||||
KeyBuf[I] = 0x5c;
|
||||
|
||||
sha256_init(&RCtx);
|
||||
sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
|
||||
}
|
||||
|
||||
if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse.
|
||||
{
|
||||
*RCtxOpt=RCtx;
|
||||
*SetROpt=true;
|
||||
}
|
||||
|
||||
sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
|
||||
|
||||
sha256_done(&RCtx, ResDigest);
|
||||
}
|
||||
|
||||
|
||||
// PBKDF2 for 32 byte key length. We generate the key for specified number
|
||||
// of iteration count also as two supplementary values (key for checksums
|
||||
// and password verification) for iterations+16 and iterations+32.
|
||||
void pbkdf2(const byte *Pwd, size_t PwdLength,
|
||||
const byte *Salt, size_t SaltLength,
|
||||
byte *Key, byte *V1, byte *V2, uint Count)
|
||||
{
|
||||
const size_t MaxSalt=64;
|
||||
byte SaltData[MaxSalt+4];
|
||||
memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
|
||||
|
||||
SaltData[SaltLength + 0] = 0; // Block index appened to salt.
|
||||
SaltData[SaltLength + 1] = 0; //
|
||||
SaltData[SaltLength + 2] = 0; // Since we do not request the key width
|
||||
SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1.
|
||||
|
||||
// First iteration: HMAC of password, salt and block index (1).
|
||||
byte U1[SHA256_DIGEST_SIZE];
|
||||
hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL);
|
||||
byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
|
||||
memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
|
||||
|
||||
uint CurCount[] = { Count-1, 16, 16 };
|
||||
byte *CurValue[] = { Key , V1, V2 };
|
||||
|
||||
sha256_context ICtxOpt,RCtxOpt;
|
||||
bool SetIOpt=false,SetROpt=false;
|
||||
|
||||
byte U2[SHA256_DIGEST_SIZE];
|
||||
for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
|
||||
{
|
||||
for (uint J = 0; J < CurCount[I]; J++)
|
||||
{
|
||||
// U2 = PRF (P, U1).
|
||||
hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt);
|
||||
memcpy(U1, U2, sizeof(U1));
|
||||
for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
|
||||
Fn[K] ^= U1[K];
|
||||
}
|
||||
memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
cleandata(SaltData, sizeof(SaltData));
|
||||
cleandata(Fn, sizeof(Fn));
|
||||
cleandata(U1, sizeof(U1));
|
||||
cleandata(U2, sizeof(U2));
|
||||
}
|
||||
|
||||
|
||||
bool CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
|
||||
const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
|
||||
byte *PswCheck)
|
||||
{
|
||||
if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
|
||||
return false;
|
||||
|
||||
byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
|
||||
bool Found=false;
|
||||
for (uint I=0;I<ASIZE(KDF5Cache);I++)
|
||||
{
|
||||
KDF5CacheItem *Item=KDF5Cache+I;
|
||||
if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt &&
|
||||
memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
|
||||
{
|
||||
memcpy(Key,Item->Key,sizeof(Key));
|
||||
SecHideData(Key,sizeof(Key),false,false);
|
||||
|
||||
memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
|
||||
memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
|
||||
Found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
{
|
||||
char PwdUtf[MAXPASSWORD*4];
|
||||
WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
|
||||
|
||||
pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt));
|
||||
cleandata(PwdUtf,sizeof(PwdUtf));
|
||||
|
||||
KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache));
|
||||
Item->Lg2Count=Lg2Cnt;
|
||||
Item->Pwd=*Password;
|
||||
memcpy(Item->Salt,Salt,SIZE_SALT50);
|
||||
memcpy(Item->Key,Key,sizeof(Item->Key));
|
||||
memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
|
||||
memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
|
||||
SecHideData(Item->Key,sizeof(Item->Key),true,false);
|
||||
}
|
||||
if (HashKey!=NULL)
|
||||
memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
|
||||
if (PswCheck!=NULL)
|
||||
{
|
||||
memset(PswCheck,0,SIZE_PSWCHECK);
|
||||
for (uint I=0;I<SHA256_DIGEST_SIZE;I++)
|
||||
PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I];
|
||||
cleandata(PswCheckValue,sizeof(PswCheckValue));
|
||||
}
|
||||
|
||||
// NULL initialization vector is possible if we only need the password
|
||||
// check value for archive encryption header.
|
||||
if (InitV!=NULL)
|
||||
rin.Init(Encrypt, Key, 256, InitV);
|
||||
|
||||
cleandata(Key,sizeof(Key));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ConvertHashToMAC(HashValue *Value,byte *Key)
|
||||
{
|
||||
if (Value->Type==HASH_CRC32)
|
||||
{
|
||||
byte RawCRC[4];
|
||||
RawPut4(Value->CRC32,RawCRC);
|
||||
byte Digest[SHA256_DIGEST_SIZE];
|
||||
hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL);
|
||||
Value->CRC32=0;
|
||||
for (uint I=0;I<ASIZE(Digest);I++)
|
||||
Value->CRC32^=Digest[I] << ((I & 3) * 8);
|
||||
Value->CRC32&=0xffffffff; // In case the variable size is larger than 32-bit.
|
||||
}
|
||||
if (Value->Type==HASH_BLAKE2)
|
||||
{
|
||||
byte Digest[BLAKE2_DIGEST_SIZE];
|
||||
hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL);
|
||||
memcpy(Value->Digest,Digest,sizeof(Value->Digest));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static void TestPBKDF2();
|
||||
struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
|
||||
|
||||
void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
|
||||
{
|
||||
byte Key[32],V1[32],V2[32];
|
||||
|
||||
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
|
||||
byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
|
||||
mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
|
||||
|
||||
pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
|
||||
byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
|
||||
mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
|
||||
|
||||
pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
|
||||
byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
|
||||
mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
|
||||
}
|
||||
#endif
|
||||
Vendored
+513
@@ -0,0 +1,513 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
static int RarErrorToDll(RAR_EXIT ErrCode);
|
||||
|
||||
struct DataSet
|
||||
{
|
||||
CommandData Cmd;
|
||||
Archive Arc;
|
||||
CmdExtract Extract;
|
||||
int OpenMode;
|
||||
int HeaderSize;
|
||||
|
||||
DataSet():Arc(&Cmd),Extract(&Cmd) {};
|
||||
};
|
||||
|
||||
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
|
||||
{
|
||||
RAROpenArchiveDataEx rx{};
|
||||
rx.ArcName=r->ArcName;
|
||||
rx.OpenMode=r->OpenMode;
|
||||
rx.CmtBuf=r->CmtBuf;
|
||||
rx.CmtBufSize=r->CmtBufSize;
|
||||
HANDLE hArc=RAROpenArchiveEx(&rx);
|
||||
r->OpenResult=rx.OpenResult;
|
||||
r->CmtSize=rx.CmtSize;
|
||||
r->CmtState=rx.CmtState;
|
||||
return hArc;
|
||||
}
|
||||
|
||||
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
|
||||
{
|
||||
DataSet *Data=nullptr;
|
||||
try
|
||||
{
|
||||
ErrHandler.Clean();
|
||||
|
||||
r->OpenResult=0;
|
||||
Data=new DataSet;
|
||||
Data->Cmd.DllError=0;
|
||||
Data->OpenMode=r->OpenMode;
|
||||
Data->Cmd.FileArgs.AddString(L"*");
|
||||
Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
|
||||
|
||||
std::string AnsiArcName;
|
||||
if (r->ArcName!=nullptr)
|
||||
{
|
||||
AnsiArcName=r->ArcName;
|
||||
#ifdef _WIN_ALL
|
||||
if (!AreFileApisANSI())
|
||||
OemToExt(r->ArcName,AnsiArcName);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::wstring ArcName;
|
||||
if (r->ArcNameW!=nullptr && *r->ArcNameW!=0)
|
||||
ArcName=r->ArcNameW;
|
||||
else
|
||||
CharToWide(AnsiArcName,ArcName);
|
||||
|
||||
Data->Cmd.AddArcName(ArcName);
|
||||
Data->Cmd.Overwrite=OVERWRITE_ALL;
|
||||
Data->Cmd.VersionControl=1;
|
||||
|
||||
Data->Cmd.Callback=r->Callback;
|
||||
Data->Cmd.UserData=r->UserData;
|
||||
|
||||
// Open shared mode is added by request of dll users, who need to
|
||||
// browse and unpack archives while downloading.
|
||||
Data->Cmd.OpenShared = true;
|
||||
if (!Data->Arc.Open(ArcName,FMF_OPENSHARED))
|
||||
{
|
||||
r->OpenResult=ERAR_EOPEN;
|
||||
delete Data;
|
||||
return nullptr;
|
||||
}
|
||||
if (!Data->Arc.IsArchive(true))
|
||||
{
|
||||
if (Data->Cmd.DllError!=0)
|
||||
r->OpenResult=Data->Cmd.DllError;
|
||||
else
|
||||
{
|
||||
RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
|
||||
if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING)
|
||||
r->OpenResult=RarErrorToDll(ErrCode);
|
||||
else
|
||||
r->OpenResult=ERAR_BAD_ARCHIVE;
|
||||
}
|
||||
delete Data;
|
||||
return nullptr;
|
||||
}
|
||||
r->Flags=0;
|
||||
|
||||
if (Data->Arc.Volume)
|
||||
r->Flags|=ROADF_VOLUME;
|
||||
if (Data->Arc.MainComment)
|
||||
r->Flags|=ROADF_COMMENT;
|
||||
if (Data->Arc.Locked)
|
||||
r->Flags|=ROADF_LOCK;
|
||||
if (Data->Arc.Solid)
|
||||
r->Flags|=ROADF_SOLID;
|
||||
if (Data->Arc.NewNumbering)
|
||||
r->Flags|=ROADF_NEWNUMBERING;
|
||||
if (Data->Arc.Signed)
|
||||
r->Flags|=ROADF_SIGNED;
|
||||
if (Data->Arc.Protected)
|
||||
r->Flags|=ROADF_RECOVERY;
|
||||
if (Data->Arc.Encrypted)
|
||||
r->Flags|=ROADF_ENCHEADERS;
|
||||
if (Data->Arc.FirstVolume)
|
||||
r->Flags|=ROADF_FIRSTVOLUME;
|
||||
|
||||
std::wstring CmtDataW;
|
||||
if (r->CmtBufSize!=0 && Data->Arc.GetComment(CmtDataW))
|
||||
{
|
||||
if (r->CmtBufW!=nullptr)
|
||||
{
|
||||
// CmtDataW.push_back(0);
|
||||
size_t Size=wcslen(CmtDataW.data())+1;
|
||||
|
||||
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
|
||||
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
|
||||
memcpy(r->CmtBufW,CmtDataW.data(),(r->CmtSize-1)*sizeof(*r->CmtBufW));
|
||||
r->CmtBufW[r->CmtSize-1]=0;
|
||||
}
|
||||
else
|
||||
if (r->CmtBuf!=NULL)
|
||||
{
|
||||
std::vector<char> CmtData(CmtDataW.size()*4+1);
|
||||
WideToChar(&CmtDataW[0],&CmtData[0],CmtData.size()-1);
|
||||
size_t Size=strlen(CmtData.data())+1;
|
||||
|
||||
r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
|
||||
r->CmtSize=(uint)Min(Size,r->CmtBufSize);
|
||||
memcpy(r->CmtBuf,CmtData.data(),r->CmtSize-1);
|
||||
r->CmtBuf[r->CmtSize-1]=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
r->CmtState=r->CmtSize=0;
|
||||
|
||||
#ifdef PROPAGATE_MOTW
|
||||
if (r->MarkOfTheWeb!=nullptr)
|
||||
{
|
||||
Data->Cmd.MotwAllFields=r->MarkOfTheWeb[0]=='1';
|
||||
const wchar *Sep=wcschr(r->MarkOfTheWeb,'=');
|
||||
if (r->MarkOfTheWeb[0]=='-')
|
||||
Data->Cmd.MotwList.Reset();
|
||||
else
|
||||
Data->Cmd.GetBriefMaskList(Sep==nullptr ? L"*":Sep+1,Data->Cmd.MotwList);
|
||||
}
|
||||
#endif
|
||||
|
||||
Data->Extract.ExtractArchiveInit(Data->Arc);
|
||||
return (HANDLE)Data;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
if (Data!=NULL && Data->Cmd.DllError!=0)
|
||||
r->OpenResult=Data->Cmd.DllError;
|
||||
else
|
||||
r->OpenResult=RarErrorToDll(ErrCode);
|
||||
if (Data != NULL)
|
||||
delete Data;
|
||||
return NULL;
|
||||
}
|
||||
catch (std::bad_alloc&) // Catch 'new' exception.
|
||||
{
|
||||
r->OpenResult=ERAR_NO_MEMORY;
|
||||
if (Data != NULL)
|
||||
delete Data;
|
||||
}
|
||||
return NULL; // To make compilers happy.
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
try
|
||||
{
|
||||
bool Success=Data==NULL ? false:Data->Arc.Close();
|
||||
delete Data;
|
||||
return Success ? ERAR_SUCCESS : ERAR_ECLOSE;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
|
||||
{
|
||||
struct RARHeaderDataEx X{};
|
||||
|
||||
int Code=RARReadHeaderEx(hArcData,&X);
|
||||
|
||||
strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName));
|
||||
strncpyz(D->FileName,X.FileName,ASIZE(D->FileName));
|
||||
D->Flags=X.Flags;
|
||||
D->PackSize=X.PackSize;
|
||||
D->UnpSize=X.UnpSize;
|
||||
D->HostOS=X.HostOS;
|
||||
D->FileCRC=X.FileCRC;
|
||||
D->FileTime=X.FileTime;
|
||||
D->UnpVer=X.UnpVer;
|
||||
D->Method=X.Method;
|
||||
D->FileAttr=X.FileAttr;
|
||||
D->CmtSize=0;
|
||||
D->CmtState=0;
|
||||
|
||||
return Code;
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
try
|
||||
{
|
||||
if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
|
||||
{
|
||||
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
|
||||
Data->Arc.EndArcHead.NextVolume)
|
||||
if (MergeArchive(Data->Arc,NULL,false,'L'))
|
||||
{
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
return RARReadHeaderEx(hArcData,D);
|
||||
}
|
||||
else
|
||||
return ERAR_EOPEN;
|
||||
|
||||
if (Data->Arc.BrokenHeader)
|
||||
return ERAR_BAD_DATA;
|
||||
|
||||
// Might be necessary if RARSetPassword is still called instead of
|
||||
// open callback for RAR5 archives and if password is invalid.
|
||||
if (Data->Arc.FailedHeaderDecryption)
|
||||
return ERAR_BAD_PASSWORD;
|
||||
|
||||
return ERAR_END_ARCHIVE;
|
||||
}
|
||||
FileHeader *hd=&Data->Arc.FileHead;
|
||||
if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore)
|
||||
{
|
||||
int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
|
||||
if (Code==0)
|
||||
return RARReadHeaderEx(hArcData,D);
|
||||
else
|
||||
return Code;
|
||||
}
|
||||
wcsncpyz(D->ArcNameW,Data->Arc.FileName.c_str(),ASIZE(D->ArcNameW));
|
||||
WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
|
||||
if (D->ArcNameEx!=nullptr)
|
||||
wcsncpyz(D->ArcNameEx,Data->Arc.FileName.c_str(),D->ArcNameExSize);
|
||||
|
||||
wcsncpyz(D->FileNameW,hd->FileName.c_str(),ASIZE(D->FileNameW));
|
||||
WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
|
||||
#ifdef _WIN_ALL
|
||||
CharToOemA(D->FileName,D->FileName);
|
||||
#endif
|
||||
if (D->FileNameEx!=nullptr)
|
||||
wcsncpyz(D->FileNameEx,hd->FileName.c_str(),D->FileNameExSize);
|
||||
|
||||
D->Flags=0;
|
||||
if (hd->SplitBefore)
|
||||
D->Flags|=RHDF_SPLITBEFORE;
|
||||
if (hd->SplitAfter)
|
||||
D->Flags|=RHDF_SPLITAFTER;
|
||||
if (hd->Encrypted)
|
||||
D->Flags|=RHDF_ENCRYPTED;
|
||||
if (hd->Solid)
|
||||
D->Flags|=RHDF_SOLID;
|
||||
if (hd->Dir)
|
||||
D->Flags|=RHDF_DIRECTORY;
|
||||
|
||||
D->PackSize=uint(hd->PackSize & 0xffffffff);
|
||||
D->PackSizeHigh=uint(hd->PackSize>>32);
|
||||
D->UnpSize=uint(hd->UnpSize & 0xffffffff);
|
||||
D->UnpSizeHigh=uint(hd->UnpSize>>32);
|
||||
D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
|
||||
D->UnpVer=Data->Arc.FileHead.UnpVer;
|
||||
D->FileCRC=hd->FileHash.CRC32;
|
||||
D->FileTime=hd->mtime.GetDos();
|
||||
|
||||
uint64 MRaw=hd->mtime.GetWin();
|
||||
D->MtimeLow=(uint)MRaw;
|
||||
D->MtimeHigh=(uint)(MRaw>>32);
|
||||
uint64 CRaw=hd->ctime.GetWin();
|
||||
D->CtimeLow=(uint)CRaw;
|
||||
D->CtimeHigh=(uint)(CRaw>>32);
|
||||
uint64 ARaw=hd->atime.GetWin();
|
||||
D->AtimeLow=(uint)ARaw;
|
||||
D->AtimeHigh=(uint)(ARaw>>32);
|
||||
|
||||
D->Method=hd->Method+0x30;
|
||||
D->FileAttr=hd->FileAttr;
|
||||
D->CmtSize=0;
|
||||
D->CmtState=0;
|
||||
|
||||
D->DictSize=uint(hd->WinSize/1024);
|
||||
|
||||
switch (hd->FileHash.Type)
|
||||
{
|
||||
case HASH_RAR14:
|
||||
case HASH_CRC32:
|
||||
D->HashType=RAR_HASH_CRC32;
|
||||
break;
|
||||
case HASH_BLAKE2:
|
||||
D->HashType=RAR_HASH_BLAKE2;
|
||||
memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
|
||||
break;
|
||||
default:
|
||||
D->HashType=RAR_HASH_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
D->RedirType=hd->RedirType;
|
||||
// RedirNameSize sanity check is useful in case some developer
|
||||
// did not initialize Reserved area with 0 as required in docs.
|
||||
// We have taken 'Redir*' fields from Reserved area. We may remove
|
||||
// this RedirNameSize check sometimes later.
|
||||
if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
|
||||
D->RedirNameSize>0 && D->RedirNameSize<100000)
|
||||
wcsncpyz(D->RedirName,hd->RedirName.c_str(),D->RedirNameSize);
|
||||
D->DirTarget=hd->DirTarget;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
|
||||
}
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
|
||||
}
|
||||
|
||||
|
||||
int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
try
|
||||
{
|
||||
Data->Cmd.DllError=0;
|
||||
if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
|
||||
Operation==RAR_SKIP && !Data->Arc.Solid)
|
||||
{
|
||||
if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
|
||||
Data->Arc.FileHead.SplitAfter)
|
||||
if (MergeArchive(Data->Arc,NULL,false,'L'))
|
||||
{
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
return ERAR_SUCCESS;
|
||||
}
|
||||
else
|
||||
return ERAR_EOPEN;
|
||||
Data->Arc.SeekToNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
Data->Cmd.DllOpMode=Operation;
|
||||
|
||||
Data->Cmd.ExtrPath.clear();
|
||||
Data->Cmd.DllDestName.clear();
|
||||
|
||||
if (DestPath!=NULL)
|
||||
{
|
||||
std::string ExtrPathA=DestPath;
|
||||
#ifdef _WIN_ALL
|
||||
// We must not apply OemToCharBuffA directly to DestPath,
|
||||
// because we do not know DestPath length and OemToCharBuffA
|
||||
// does not stop at 0.
|
||||
OemToExt(ExtrPathA,ExtrPathA);
|
||||
#endif
|
||||
CharToWide(ExtrPathA,Data->Cmd.ExtrPath);
|
||||
AddEndSlash(Data->Cmd.ExtrPath);
|
||||
}
|
||||
if (DestName!=NULL)
|
||||
{
|
||||
std::string DestNameA=DestName;
|
||||
#ifdef _WIN_ALL
|
||||
// We must not apply OemToCharBuffA directly to DestName,
|
||||
// because we do not know DestName length and OemToCharBuffA
|
||||
// does not stop at 0.
|
||||
OemToExt(DestNameA,DestNameA);
|
||||
#endif
|
||||
CharToWide(DestNameA,Data->Cmd.DllDestName);
|
||||
}
|
||||
|
||||
if (DestPathW!=NULL)
|
||||
{
|
||||
Data->Cmd.ExtrPath=DestPathW;
|
||||
AddEndSlash(Data->Cmd.ExtrPath);
|
||||
}
|
||||
|
||||
if (DestNameW!=NULL)
|
||||
Data->Cmd.DllDestName=DestNameW;
|
||||
|
||||
Data->Cmd.Command=Operation==RAR_EXTRACT ? L"X":L"T";
|
||||
Data->Cmd.Test=Operation!=RAR_EXTRACT;
|
||||
bool Repeat=false;
|
||||
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
|
||||
|
||||
// Now we process extra file information if any.
|
||||
// It is important to do it in the same ProcessFile(), because caller
|
||||
// app can rely on this behavior, for example, to overwrite
|
||||
// the extracted Mark of the Web with propagated from archive
|
||||
// immediately after ProcessFile() call.
|
||||
//
|
||||
// Archive can be closed if we process volumes, next volume is missing
|
||||
// and current one is already removed or deleted. So we need to check
|
||||
// if archive is still open to avoid calling file operations on
|
||||
// the invalid file handle. Some of our file operations like Seek()
|
||||
// process such invalid handle correctly, some not.
|
||||
while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
|
||||
Data->Arc.GetHeaderType()==HEAD_SERVICE)
|
||||
{
|
||||
Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
|
||||
Data->Arc.SeekToNext();
|
||||
}
|
||||
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
return ERAR_NO_MEMORY;
|
||||
}
|
||||
catch (RAR_EXIT ErrCode)
|
||||
{
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
|
||||
}
|
||||
return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrHandler.GetErrorCode());
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
|
||||
{
|
||||
return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL);
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
|
||||
{
|
||||
return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName);
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.ChangeVolProc=ChangeVolProc;
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.Callback=Callback;
|
||||
Data->Cmd.UserData=UserData;
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc)
|
||||
{
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
Data->Cmd.ProcessDataProc=ProcessDataProc;
|
||||
}
|
||||
|
||||
|
||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
|
||||
{
|
||||
#ifndef RAR_NOCRYPT
|
||||
DataSet *Data=(DataSet *)hArcData;
|
||||
wchar PasswordW[MAXPASSWORD];
|
||||
CharToWide(Password,PasswordW,ASIZE(PasswordW));
|
||||
Data->Cmd.Password.Set(PasswordW);
|
||||
cleandata(PasswordW,sizeof(PasswordW));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PASCAL RARGetDllVersion()
|
||||
{
|
||||
return RAR_DLL_VERSION;
|
||||
}
|
||||
|
||||
|
||||
static int RarErrorToDll(RAR_EXIT ErrCode)
|
||||
{
|
||||
switch(ErrCode)
|
||||
{
|
||||
case RARX_FATAL:
|
||||
case RARX_READ:
|
||||
return ERAR_EREAD;
|
||||
case RARX_CRC:
|
||||
return ERAR_BAD_DATA;
|
||||
case RARX_WRITE:
|
||||
return ERAR_EWRITE;
|
||||
case RARX_OPEN:
|
||||
return ERAR_EOPEN;
|
||||
case RARX_CREATE:
|
||||
return ERAR_ECREATE;
|
||||
case RARX_MEMORY:
|
||||
return ERAR_NO_MEMORY;
|
||||
case RARX_BADPWD:
|
||||
return ERAR_BAD_PASSWORD;
|
||||
case RARX_SUCCESS:
|
||||
return ERAR_SUCCESS; // 0.
|
||||
case RARX_BADARC:
|
||||
return ERAR_BAD_ARCHIVE;
|
||||
default:
|
||||
return ERAR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
EXPORTS
|
||||
RAROpenArchive
|
||||
RAROpenArchiveEx
|
||||
RARCloseArchive
|
||||
RARReadHeader
|
||||
RARReadHeaderEx
|
||||
RARProcessFile
|
||||
RARProcessFileW
|
||||
RARSetCallback
|
||||
RARSetChangeVolProc
|
||||
RARSetProcessDataProc
|
||||
RARSetPassword
|
||||
RARGetDllVersion
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
#ifndef _UNRAR_DLL_
|
||||
#define _UNRAR_DLL_
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#define ERAR_SUCCESS 0
|
||||
#define ERAR_END_ARCHIVE 10
|
||||
#define ERAR_NO_MEMORY 11
|
||||
#define ERAR_BAD_DATA 12
|
||||
#define ERAR_BAD_ARCHIVE 13
|
||||
#define ERAR_UNKNOWN_FORMAT 14
|
||||
#define ERAR_EOPEN 15
|
||||
#define ERAR_ECREATE 16
|
||||
#define ERAR_ECLOSE 17
|
||||
#define ERAR_EREAD 18
|
||||
#define ERAR_EWRITE 19
|
||||
#define ERAR_SMALL_BUF 20
|
||||
#define ERAR_UNKNOWN 21
|
||||
#define ERAR_MISSING_PASSWORD 22
|
||||
#define ERAR_EREFERENCE 23
|
||||
#define ERAR_BAD_PASSWORD 24
|
||||
#define ERAR_LARGE_DICT 25
|
||||
|
||||
#define RAR_OM_LIST 0
|
||||
#define RAR_OM_EXTRACT 1
|
||||
#define RAR_OM_LIST_INCSPLIT 2
|
||||
|
||||
#define RAR_SKIP 0
|
||||
#define RAR_TEST 1
|
||||
#define RAR_EXTRACT 2
|
||||
|
||||
#define RAR_VOL_ASK 0
|
||||
#define RAR_VOL_NOTIFY 1
|
||||
|
||||
#define RAR_DLL_VERSION 9
|
||||
|
||||
#define RAR_HASH_NONE 0
|
||||
#define RAR_HASH_CRC32 1
|
||||
#define RAR_HASH_BLAKE2 2
|
||||
|
||||
|
||||
#ifdef _UNIX
|
||||
#define CALLBACK
|
||||
#define PASCAL
|
||||
#define LONG long
|
||||
#define HANDLE void *
|
||||
#define LPARAM long
|
||||
#define UINT unsigned int
|
||||
#endif
|
||||
|
||||
#define RHDF_SPLITBEFORE 0x01
|
||||
#define RHDF_SPLITAFTER 0x02
|
||||
#define RHDF_ENCRYPTED 0x04
|
||||
#define RHDF_SOLID 0x10
|
||||
#define RHDF_DIRECTORY 0x20
|
||||
|
||||
|
||||
struct RARHeaderData
|
||||
{
|
||||
char ArcName[260];
|
||||
char FileName[260];
|
||||
unsigned int Flags;
|
||||
unsigned int PackSize;
|
||||
unsigned int UnpSize;
|
||||
unsigned int HostOS;
|
||||
unsigned int FileCRC;
|
||||
unsigned int FileTime;
|
||||
unsigned int UnpVer;
|
||||
unsigned int Method;
|
||||
unsigned int FileAttr;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
};
|
||||
|
||||
|
||||
struct RARHeaderDataEx
|
||||
{
|
||||
char ArcName[1024];
|
||||
wchar_t ArcNameW[1024];
|
||||
char FileName[1024];
|
||||
wchar_t FileNameW[1024];
|
||||
unsigned int Flags;
|
||||
unsigned int PackSize;
|
||||
unsigned int PackSizeHigh;
|
||||
unsigned int UnpSize;
|
||||
unsigned int UnpSizeHigh;
|
||||
unsigned int HostOS;
|
||||
unsigned int FileCRC;
|
||||
unsigned int FileTime;
|
||||
unsigned int UnpVer;
|
||||
unsigned int Method;
|
||||
unsigned int FileAttr;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
unsigned int DictSize;
|
||||
unsigned int HashType;
|
||||
char Hash[32];
|
||||
unsigned int RedirType;
|
||||
wchar_t *RedirName;
|
||||
unsigned int RedirNameSize;
|
||||
unsigned int DirTarget;
|
||||
unsigned int MtimeLow;
|
||||
unsigned int MtimeHigh;
|
||||
unsigned int CtimeLow;
|
||||
unsigned int CtimeHigh;
|
||||
unsigned int AtimeLow;
|
||||
unsigned int AtimeHigh;
|
||||
wchar_t *ArcNameEx;
|
||||
unsigned int ArcNameExSize;
|
||||
wchar_t *FileNameEx;
|
||||
unsigned int FileNameExSize;
|
||||
unsigned int Reserved[982];
|
||||
};
|
||||
|
||||
|
||||
struct RAROpenArchiveData
|
||||
{
|
||||
char *ArcName;
|
||||
unsigned int OpenMode;
|
||||
unsigned int OpenResult;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
};
|
||||
|
||||
typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
|
||||
|
||||
#define ROADF_VOLUME 0x0001
|
||||
#define ROADF_COMMENT 0x0002
|
||||
#define ROADF_LOCK 0x0004
|
||||
#define ROADF_SOLID 0x0008
|
||||
#define ROADF_NEWNUMBERING 0x0010
|
||||
#define ROADF_SIGNED 0x0020
|
||||
#define ROADF_RECOVERY 0x0040
|
||||
#define ROADF_ENCHEADERS 0x0080
|
||||
#define ROADF_FIRSTVOLUME 0x0100
|
||||
|
||||
#define ROADOF_KEEPBROKEN 0x0001
|
||||
|
||||
struct RAROpenArchiveDataEx
|
||||
{
|
||||
char *ArcName;
|
||||
wchar_t *ArcNameW;
|
||||
unsigned int OpenMode;
|
||||
unsigned int OpenResult;
|
||||
char *CmtBuf;
|
||||
unsigned int CmtBufSize;
|
||||
unsigned int CmtSize;
|
||||
unsigned int CmtState;
|
||||
unsigned int Flags;
|
||||
UNRARCALLBACK Callback;
|
||||
LPARAM UserData;
|
||||
unsigned int OpFlags;
|
||||
wchar_t *CmtBufW;
|
||||
wchar_t *MarkOfTheWeb;
|
||||
unsigned int Reserved[23];
|
||||
};
|
||||
|
||||
enum UNRARCALLBACK_MESSAGES {
|
||||
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
|
||||
UCM_NEEDPASSWORDW,UCM_LARGEDICT
|
||||
};
|
||||
|
||||
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
|
||||
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData);
|
||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData);
|
||||
int PASCAL RARCloseArchive(HANDLE hArcData);
|
||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData);
|
||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
|
||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
|
||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName);
|
||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData);
|
||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc);
|
||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc);
|
||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password);
|
||||
int PASCAL RARGetDllVersion();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 7, 20, 1, 1764
|
||||
PRODUCTVERSION 7, 20, 1, 1764
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
{
|
||||
BLOCK "StringFileInfo"
|
||||
{
|
||||
BLOCK "040904E4"
|
||||
{
|
||||
VALUE "CompanyName", "Alexander Roshal\0"
|
||||
VALUE "ProductName", "RAR decompression library\0"
|
||||
VALUE "FileDescription", "RAR decompression library\0"
|
||||
VALUE "FileVersion", "7.20.1\0"
|
||||
VALUE "ProductVersion", "7.20.1\0"
|
||||
VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2025\0"
|
||||
VALUE "OriginalFilename", "Unrar.dll\0"
|
||||
}
|
||||
}
|
||||
BLOCK "VarFileInfo"
|
||||
{
|
||||
VALUE "Translation", 0x0409, 0x04E4
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
EXPORTS
|
||||
RAROpenArchive
|
||||
RAROpenArchiveEx
|
||||
RARCloseArchive
|
||||
RARReadHeader
|
||||
RARReadHeaderEx
|
||||
RARProcessFile
|
||||
RARProcessFileW
|
||||
RARSetCallback
|
||||
RARSetChangeVolProc
|
||||
RARSetProcessDataProc
|
||||
; RARSetPassword
|
||||
RARGetDllVersion
|
||||
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
EncodeFileName::EncodeFileName()
|
||||
{
|
||||
Flags=0;
|
||||
FlagBits=0;
|
||||
FlagsPos=0;
|
||||
DestSize=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void EncodeFileName::Decode(const char *Name,size_t NameSize,
|
||||
const byte *EncName,size_t EncSize,
|
||||
std::wstring &NameW)
|
||||
{
|
||||
size_t EncPos=0,DecPos=0;
|
||||
byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0;
|
||||
while (EncPos<EncSize)
|
||||
{
|
||||
if (FlagBits==0)
|
||||
{
|
||||
Flags=EncName[EncPos++];
|
||||
FlagBits=8;
|
||||
}
|
||||
switch(Flags>>6)
|
||||
{
|
||||
case 0:
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
// We need DecPos also for ASCII "Name", so resize() instead of push_back().
|
||||
NameW.resize(DecPos+1);
|
||||
NameW[DecPos++]=EncName[EncPos++];
|
||||
break;
|
||||
case 1:
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
NameW.resize(DecPos+1);
|
||||
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
|
||||
break;
|
||||
case 2:
|
||||
if (EncPos+1>=EncSize)
|
||||
break;
|
||||
NameW.resize(DecPos+1);
|
||||
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
|
||||
EncPos+=2;
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
int Length=EncName[EncPos++];
|
||||
if ((Length & 0x80)!=0)
|
||||
{
|
||||
if (EncPos>=EncSize)
|
||||
break;
|
||||
byte Correction=EncName[EncPos++];
|
||||
for (Length=(Length&0x7f)+2;Length>0 && DecPos<NameSize;Length--,DecPos++)
|
||||
{
|
||||
NameW.resize(DecPos+1);
|
||||
NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8);
|
||||
}
|
||||
}
|
||||
else
|
||||
for (Length+=2;Length>0 && DecPos<NameSize;Length--,DecPos++)
|
||||
{
|
||||
NameW.resize(DecPos+1);
|
||||
NameW[DecPos]=Name[DecPos];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Flags<<=2;
|
||||
FlagBits-=2;
|
||||
}
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
#ifndef _RAR_ENCNAME_
|
||||
#define _RAR_ENCNAME_
|
||||
|
||||
class EncodeFileName
|
||||
{
|
||||
private:
|
||||
void AddFlags(byte Value,std::vector<byte> &EncName);
|
||||
|
||||
byte Flags;
|
||||
uint FlagBits;
|
||||
size_t FlagsPos;
|
||||
size_t DestSize;
|
||||
public:
|
||||
EncodeFileName();
|
||||
void Encode(const std::string &Name,const std::wstring &NameW,std::vector<byte> &EncName);
|
||||
void Decode(const char *Name,size_t NameSize,const byte *EncName,size_t EncSize,std::wstring &NameW);
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+433
@@ -0,0 +1,433 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
ErrorHandler::ErrorHandler()
|
||||
{
|
||||
Clean();
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Clean()
|
||||
{
|
||||
ExitCode=RARX_SUCCESS;
|
||||
ErrCount=0;
|
||||
EnableBreak=true;
|
||||
Silent=false;
|
||||
UserBreak=false;
|
||||
MainExit=false;
|
||||
DisableShutdown=false;
|
||||
ReadErrIgnoreAll=false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::MemoryError()
|
||||
{
|
||||
MemoryErrorMsg();
|
||||
Exit(RARX_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenError(const std::wstring &FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
OpenErrorMsg(FileName);
|
||||
Exit(RARX_OPEN);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CloseError(const std::wstring &FileName)
|
||||
{
|
||||
if (!UserBreak)
|
||||
{
|
||||
uiMsg(UIERROR_FILECLOSE,FileName);
|
||||
SysErrMsg();
|
||||
}
|
||||
// We must not call Exit and throw an exception here, because this function
|
||||
// is called from File object destructor and can be invoked when stack
|
||||
// unwinding while handling another exception. Throwing a new exception
|
||||
// when stack unwinding is prohibited and terminates a program.
|
||||
// If necessary, we can check std::uncaught_exception() before throw.
|
||||
SetErrorCode(RARX_FATAL);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadError(const std::wstring &FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
ReadErrorMsg(FileName);
|
||||
#endif
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit)
|
||||
{
|
||||
SetErrorCode(RARX_READ);
|
||||
#if !defined(SILENT) && !defined(SFX_MODULE)
|
||||
if (!Silent)
|
||||
{
|
||||
uiMsg(UIERROR_FILEREAD,L"",FileName);
|
||||
SysErrMsg();
|
||||
if (ReadErrIgnoreAll)
|
||||
Ignore=true;
|
||||
else
|
||||
{
|
||||
bool All=false;
|
||||
uiAskRepeatRead(FileName,Ignore,All,Retry,Quit);
|
||||
if (All)
|
||||
ReadErrIgnoreAll=Ignore=true;
|
||||
if (Quit) // Disable shutdown if user select Quit in read error prompt.
|
||||
DisableShutdown=true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice.
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::WriteError(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
#ifndef SILENT
|
||||
WriteErrorMsg(ArcName,FileName);
|
||||
#endif
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
void ErrorHandler::WriteErrorFAT(const std::wstring &FileName)
|
||||
{
|
||||
SysErrMsg();
|
||||
uiMsg(UIERROR_NTFSREQUIRED,FileName);
|
||||
#if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL)
|
||||
Exit(RARX_WRITE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool ErrorHandler::AskRepeatWrite(const std::wstring &FileName,bool DiskFull)
|
||||
{
|
||||
#ifndef SILENT
|
||||
if (!Silent)
|
||||
{
|
||||
// We do not display "repeat write" prompt in Android, so we do not
|
||||
// need the matching system error message.
|
||||
SysErrMsg();
|
||||
bool Repeat=uiAskRepeatWrite(FileName,DiskFull);
|
||||
if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog.
|
||||
DisableShutdown=true;
|
||||
return Repeat;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SeekError(const std::wstring &FileName)
|
||||
{
|
||||
if (!UserBreak)
|
||||
{
|
||||
uiMsg(UIERROR_FILESEEK,FileName);
|
||||
SysErrMsg();
|
||||
}
|
||||
#if !defined(SILENT) || defined(RARDLL)
|
||||
Exit(RARX_FATAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::GeneralErrMsg(const wchar *fmt,...)
|
||||
{
|
||||
#ifndef RARDLL
|
||||
va_list arglist;
|
||||
va_start(arglist,fmt);
|
||||
|
||||
std::wstring Msg=vwstrprintf(fmt,arglist);
|
||||
uiMsg(UIERROR_GENERALERRMSG,Msg);
|
||||
SysErrMsg();
|
||||
|
||||
va_end(arglist);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::MemoryErrorMsg()
|
||||
{
|
||||
uiMsg(UIERROR_MEMORY);
|
||||
SetErrorCode(RARX_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenErrorMsg(const std::wstring &FileName)
|
||||
{
|
||||
OpenErrorMsg(L"",FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEOPEN,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_OPEN);
|
||||
|
||||
// Keep GUI responsive if many files cannot be opened when archiving.
|
||||
// Call after SysErrMsg to avoid modifying the error code and SysErrMsg text.
|
||||
Wait();
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CreateErrorMsg(const std::wstring &FileName)
|
||||
{
|
||||
CreateErrorMsg(L"",FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILECREATE,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadErrorMsg(const std::wstring &FileName)
|
||||
{
|
||||
ReadErrorMsg(L"",FileName);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEREAD,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_READ);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
|
||||
SysErrMsg();
|
||||
SetErrorCode(RARX_WRITE);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ArcBrokenMsg(const std::wstring &ArcName)
|
||||
{
|
||||
uiMsg(UIERROR_ARCBROKEN,ArcName);
|
||||
SetErrorCode(RARX_CRC);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
|
||||
SetErrorCode(RARX_CRC);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName)
|
||||
{
|
||||
uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
|
||||
ErrHandler.SetErrorCode(RARX_FATAL);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Exit(RAR_EXIT ExitCode)
|
||||
{
|
||||
uiAlarm(UIALARM_ERROR);
|
||||
Throw(ExitCode);
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetErrorCode(RAR_EXIT Code)
|
||||
{
|
||||
switch(Code)
|
||||
{
|
||||
case RARX_WARNING:
|
||||
case RARX_USERBREAK:
|
||||
if (ExitCode==RARX_SUCCESS)
|
||||
ExitCode=Code;
|
||||
break;
|
||||
case RARX_CRC:
|
||||
// 2025.10.25: RARX_OPEN is set if next volume is missing.
|
||||
if (ExitCode!=RARX_BADPWD && ExitCode!=RARX_OPEN)
|
||||
ExitCode=Code;
|
||||
break;
|
||||
case RARX_FATAL:
|
||||
if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING)
|
||||
ExitCode=RARX_FATAL;
|
||||
break;
|
||||
default:
|
||||
ExitCode=Code;
|
||||
break;
|
||||
}
|
||||
ErrCount++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
BOOL __stdcall ProcessSignal(DWORD SigType)
|
||||
#else
|
||||
#if defined(__sun)
|
||||
extern "C"
|
||||
#endif
|
||||
void _stdfunction ProcessSignal(int SigType)
|
||||
#endif
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// When a console application is run as a service, this allows the service
|
||||
// to continue running after the user logs off.
|
||||
if (SigType==CTRL_LOGOFF_EVENT)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
ErrHandler.UserBreak=true;
|
||||
ErrHandler.SetDisableShutdown();
|
||||
mprintf(St(MBreak));
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
// Let the main thread to handle 'throw' and destroy file objects.
|
||||
for (uint I=0;!ErrHandler.MainExit && I<50;I++)
|
||||
Sleep(100);
|
||||
#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
ExtRes.UnloadDLL();
|
||||
#endif
|
||||
exit(RARX_USERBREAK);
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
static uint BreakCount=0;
|
||||
// User continues to press Ctrl+C, exit immediately without cleanup.
|
||||
if (++BreakCount>1)
|
||||
exit(RARX_USERBREAK);
|
||||
// Otherwise return from signal handler and let Wait() function to close
|
||||
// files and quit. We cannot use the same approach as in Windows,
|
||||
// because Unix signal handler can block execution of our main code.
|
||||
#endif
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(_MSC_VER)
|
||||
// Never reached, just to avoid a compiler warning
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetSignalHandlers(bool Enable)
|
||||
{
|
||||
EnableBreak=Enable;
|
||||
#ifdef _WIN_ALL
|
||||
SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
|
||||
#else
|
||||
signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
|
||||
signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::Throw(RAR_EXIT Code)
|
||||
{
|
||||
if (Code==RARX_USERBREAK && !EnableBreak)
|
||||
return;
|
||||
#if !defined(SILENT)
|
||||
if (Code!=RARX_SUCCESS)
|
||||
if (Code==RARX_USERERROR) // Do not write "aborted" when just displaying the online help.
|
||||
mprintf(L"\n"); // For consistency with other errors, which print the final "\n".
|
||||
else
|
||||
mprintf(L"\n%s\n",St(MProgAborted));
|
||||
#endif
|
||||
SetErrorCode(Code);
|
||||
throw Code;
|
||||
}
|
||||
|
||||
|
||||
bool ErrorHandler::GetSysErrMsg(std::wstring &Msg)
|
||||
{
|
||||
#ifndef SILENT
|
||||
#ifdef _WIN_ALL
|
||||
int ErrType=GetLastError();
|
||||
if (ErrType!=0)
|
||||
{
|
||||
wchar *Buf=nullptr;
|
||||
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
|
||||
(LPTSTR)&Buf,0,NULL)!=0)
|
||||
{
|
||||
Msg=Buf;
|
||||
LocalFree(Buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
if (errno!=0)
|
||||
{
|
||||
char *err=strerror(errno);
|
||||
if (err!=NULL)
|
||||
{
|
||||
CharToWide(err,Msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SysErrMsg()
|
||||
{
|
||||
#ifndef SILENT
|
||||
std::wstring Msg;
|
||||
if (!GetSysErrMsg(Msg))
|
||||
return;
|
||||
#ifdef _WIN_ALL
|
||||
// Print string with \r\n as several strings to multiple lines.
|
||||
size_t Pos=0;
|
||||
while (Pos!=std::wstring::npos)
|
||||
{
|
||||
while (Msg[Pos]=='\r' || Msg[Pos]=='\n')
|
||||
Pos++;
|
||||
if (Pos==Msg.size())
|
||||
break;
|
||||
size_t EndPos=Msg.find_first_of(L"\r\n",Pos);
|
||||
std::wstring CurMsg=Msg.substr(Pos,EndPos==std::wstring::npos ? EndPos:EndPos-Pos);
|
||||
uiMsg(UIERROR_SYSERRMSG,CurMsg);
|
||||
Pos=EndPos;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
uiMsg(UIERROR_SYSERRMSG,Msg);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int ErrorHandler::GetSystemErrorCode()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return GetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ErrorHandler::SetSystemErrorCode(int Code)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
SetLastError(Code);
|
||||
#else
|
||||
errno=Code;
|
||||
#endif
|
||||
}
|
||||
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
#ifndef _RAR_ERRHANDLER_
|
||||
#define _RAR_ERRHANDLER_
|
||||
|
||||
enum RAR_EXIT // RAR exit code.
|
||||
{
|
||||
RARX_SUCCESS = 0,
|
||||
RARX_WARNING = 1,
|
||||
RARX_FATAL = 2,
|
||||
RARX_CRC = 3,
|
||||
RARX_LOCK = 4,
|
||||
RARX_WRITE = 5,
|
||||
RARX_OPEN = 6,
|
||||
RARX_USERERROR = 7,
|
||||
RARX_MEMORY = 8,
|
||||
RARX_CREATE = 9,
|
||||
RARX_NOFILES = 10,
|
||||
RARX_BADPWD = 11,
|
||||
RARX_READ = 12,
|
||||
RARX_BADARC = 13,
|
||||
RARX_USERBREAK = 255
|
||||
};
|
||||
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
private:
|
||||
RAR_EXIT ExitCode;
|
||||
uint ErrCount;
|
||||
bool EnableBreak;
|
||||
bool Silent;
|
||||
bool DisableShutdown; // Shutdown is not suitable after last error.
|
||||
bool ReadErrIgnoreAll;
|
||||
public:
|
||||
ErrorHandler();
|
||||
void Clean();
|
||||
void MemoryError();
|
||||
void OpenError(const std::wstring &FileName);
|
||||
void CloseError(const std::wstring &FileName);
|
||||
void ReadError(const std::wstring &FileName);
|
||||
void AskRepeatRead(const std::wstring &FileName,bool &Ignore,bool &Retry,bool &Quit);
|
||||
void WriteError(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void WriteErrorFAT(const std::wstring &FileName);
|
||||
bool AskRepeatWrite(const std::wstring &FileName,bool DiskFull);
|
||||
void SeekError(const std::wstring &FileName);
|
||||
void GeneralErrMsg(const wchar *fmt,...);
|
||||
void MemoryErrorMsg();
|
||||
void OpenErrorMsg(const std::wstring &FileName);
|
||||
void OpenErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void CreateErrorMsg(const std::wstring &FileName);
|
||||
void CreateErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void ReadErrorMsg(const std::wstring &FileName);
|
||||
void ReadErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void WriteErrorMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void ArcBrokenMsg(const std::wstring &ArcName);
|
||||
void ChecksumFailedMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void UnknownMethodMsg(const std::wstring &ArcName,const std::wstring &FileName);
|
||||
void Exit(RAR_EXIT ExitCode);
|
||||
void SetErrorCode(RAR_EXIT Code);
|
||||
RAR_EXIT GetErrorCode() {return ExitCode;}
|
||||
uint GetErrorCount() {return ErrCount;}
|
||||
void SetSignalHandlers(bool Enable);
|
||||
void Throw(RAR_EXIT Code);
|
||||
void SetSilent(bool Mode) {Silent=Mode;}
|
||||
bool GetSysErrMsg(std::wstring &Msg);
|
||||
void SysErrMsg();
|
||||
int GetSystemErrorCode();
|
||||
void SetSystemErrorCode(int Code);
|
||||
void SetDisableShutdown() {DisableShutdown=true;}
|
||||
bool IsShutdownEnabled() {return !DisableShutdown;}
|
||||
|
||||
bool UserBreak; // Ctrl+Break is pressed.
|
||||
bool MainExit; // main() is completed.
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+184
@@ -0,0 +1,184 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
#include "hardlinks.cpp"
|
||||
#include "win32stm.cpp"
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
#include "win32acl.cpp"
|
||||
#include "win32lnk.cpp"
|
||||
#endif
|
||||
|
||||
#ifdef _UNIX
|
||||
#include "uowners.cpp"
|
||||
#ifdef SAVE_LINKS
|
||||
#include "ulinks.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// RAR2 service header extra records.
|
||||
#ifndef SFX_MODULE
|
||||
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if (Cmd->Test)
|
||||
return;
|
||||
switch(Arc.SubBlockHead.SubType)
|
||||
{
|
||||
case NTACL_HEAD:
|
||||
if (Cmd->ProcessOwners)
|
||||
ExtractACL20(Arc,Name);
|
||||
break;
|
||||
case STREAM_HEAD:
|
||||
ExtractStreams20(Arc,Name);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// RAR3 and RAR5 service header extra records.
|
||||
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
|
||||
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
|
||||
ExtractUnixOwner30(Arc,Name.c_str());
|
||||
#endif
|
||||
#ifdef _WIN_ALL
|
||||
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
|
||||
ExtractACL(Arc,Name);
|
||||
if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
|
||||
ExtractStreams(Arc,Name,Cmd->Test);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Extra data stored directly in file header.
|
||||
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
|
||||
SetUnixOwner(Arc,Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Calculate the number of path components except \. and \..
|
||||
static int CalcAllowedDepth(const std::wstring &Name)
|
||||
{
|
||||
int AllowedDepth=0;
|
||||
for (size_t I=0;I<Name.size();I++)
|
||||
if (IsPathDiv(Name[I]))
|
||||
{
|
||||
bool Dot=Name[I+1]=='.' && (IsPathDiv(Name[I+2]) || Name[I+2]==0);
|
||||
bool Dot2=Name[I+1]=='.' && Name[I+2]=='.' && (IsPathDiv(Name[I+3]) || Name[I+3]==0);
|
||||
if (!Dot && !Dot2)
|
||||
AllowedDepth++;
|
||||
else
|
||||
if (Dot2)
|
||||
AllowedDepth--;
|
||||
}
|
||||
return AllowedDepth < 0 ? 0 : AllowedDepth;
|
||||
}
|
||||
|
||||
|
||||
// Check if all existing path components are directories and not links.
|
||||
static bool LinkInPath(std::wstring Path)
|
||||
{
|
||||
if (Path.empty()) // So we can safely use Path.size()-1 below.
|
||||
return false;
|
||||
for (size_t I=Path.size()-1;I>0;I--)
|
||||
if (IsPathDiv(Path[I]))
|
||||
{
|
||||
Path.erase(I);
|
||||
FindData FD;
|
||||
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName)
|
||||
{
|
||||
// Catch root dir based /path/file paths also as stuff like \\?\.
|
||||
// Do not check PrepSrcName here, it can be root based if destination path
|
||||
// is a root based.
|
||||
if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
|
||||
return false;
|
||||
|
||||
// Number of ".." in link target.
|
||||
int UpLevels=0;
|
||||
for (uint Pos=0;Pos<TargetName.size();Pos++)
|
||||
{
|
||||
bool Dot2=TargetName[Pos]=='.' && TargetName[Pos+1]=='.' &&
|
||||
(IsPathDiv(TargetName[Pos+2]) || TargetName[Pos+2]==0) &&
|
||||
(Pos==0 || IsPathDiv(TargetName[Pos-1]));
|
||||
if (Dot2)
|
||||
UpLevels++;
|
||||
}
|
||||
// If link target includes "..", it must not have another links in its
|
||||
// source path, because they can bypass our safety check. For example,
|
||||
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
|
||||
// or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
|
||||
// file "dir/lnk1/lnk2/poc.txt" last.
|
||||
// Do not confuse with link chains in target, this is in link source path.
|
||||
// It is important for Windows too, though this check can be omitted
|
||||
// if LinksToDirs is invoked in Windows as well.
|
||||
if (UpLevels>0 && LinkInPath(PrepSrcName))
|
||||
return false;
|
||||
|
||||
// We could check just prepared src name, but for extra safety
|
||||
// we check both original (as from archive header) and prepared
|
||||
// (after applying the destination path and -ep switches) names.
|
||||
|
||||
int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
|
||||
|
||||
// Remove the destination path from prepared name if any. We should not
|
||||
// count the destination path depth, because the link target must point
|
||||
// inside of this path, not outside of it.
|
||||
size_t ExtrPathLength=Cmd->ExtrPath.size();
|
||||
if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0)
|
||||
{
|
||||
while (IsPathDiv(PrepSrcName[ExtrPathLength]))
|
||||
ExtrPathLength++;
|
||||
PrepSrcName.erase(0,ExtrPathLength);
|
||||
}
|
||||
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
|
||||
|
||||
return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
|
||||
}
|
||||
|
||||
|
||||
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink)
|
||||
{
|
||||
// Returning true in Uplink indicates that link target might include ".."
|
||||
// and enables additional checks. It is ok to falsely return true here,
|
||||
// as it implies only the minor performance penalty. But we shall always
|
||||
// return true for links with ".." in target for security reason.
|
||||
|
||||
UpLink=true; // Assume the target might include potentially unsafe "..".
|
||||
#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
|
||||
if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
|
||||
UpLink=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos;
|
||||
#endif
|
||||
|
||||
#if defined(SAVE_LINKS) && defined(_UNIX)
|
||||
// For RAR 3.x archives we process links even in test mode to skip link data.
|
||||
if (Arc.Format==RARFMT15)
|
||||
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName.c_str(),UpLink);
|
||||
if (Arc.Format==RARFMT50)
|
||||
return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead);
|
||||
#elif defined(_WIN_ALL)
|
||||
// RAR 5.0 archives store link information in file header, so there is
|
||||
// no need to additionally test it if we do not create a file.
|
||||
if (Arc.Format==RARFMT50)
|
||||
return CreateReparsePoint(Cmd,LinkName.c_str(),&Arc.FileHead);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
#ifndef _RAR_EXTINFO_
|
||||
#define _RAR_EXTINFO_
|
||||
|
||||
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName);
|
||||
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &LinkName,bool &UpLink);
|
||||
#ifdef _UNIX
|
||||
void SetUnixOwner(Archive &Arc,const std::wstring &FileName);
|
||||
#endif
|
||||
|
||||
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting);
|
||||
|
||||
std::wstring GetStreamNameNTFS(Archive &Arc);
|
||||
|
||||
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
|
||||
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
|
||||
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name);
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+1768
File diff suppressed because it is too large.
Load diff
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
#ifndef _RAR_EXTRACT_
|
||||
#define _RAR_EXTRACT_
|
||||
|
||||
enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
|
||||
|
||||
class CmdExtract
|
||||
{
|
||||
private:
|
||||
struct ExtractRef
|
||||
{
|
||||
std::wstring RefName;
|
||||
std::wstring TmpName;
|
||||
uint64 RefCount;
|
||||
};
|
||||
std::vector<ExtractRef> RefList;
|
||||
|
||||
struct AnalyzeData
|
||||
{
|
||||
std::wstring StartName;
|
||||
uint64 StartPos;
|
||||
std::wstring EndName;
|
||||
uint64 EndPos;
|
||||
} Analyze;
|
||||
|
||||
bool ArcAnalyzed;
|
||||
|
||||
void FreeAnalyzeData();
|
||||
EXTRACT_ARC_CODE ExtractArchive();
|
||||
bool ExtractFileCopy(File &New,const std::wstring &ArcName,const std::wstring &RedirName,const std::wstring &NameNew,const std::wstring &NameExisting,int64 UnpSize);
|
||||
void ExtrPrepareName(Archive &Arc,const std::wstring &ArcFileName,std::wstring &DestName);
|
||||
#ifdef RARDLL
|
||||
bool ExtrDllGetPassword();
|
||||
#else
|
||||
bool ExtrGetPassword(Archive &Arc,const std::wstring &ArcFileName,RarCheckPassword *CheckPwd);
|
||||
#endif
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
|
||||
#endif
|
||||
void ExtrCreateDir(Archive &Arc,const std::wstring &ArcFileName);
|
||||
bool ExtrCreateFile(Archive &Arc,File &CurFile,bool WriteOnly);
|
||||
bool CheckUnpVer(Archive &Arc,const std::wstring &ArcFileName);
|
||||
#ifndef SFX_MODULE
|
||||
void AnalyzeArchive(const std::wstring &ArcName,bool Volume,bool NewNumbering);
|
||||
void GetFirstVolIfFullSet(const std::wstring &SrcName,bool NewNumbering,std::wstring &DestName);
|
||||
#endif
|
||||
bool CheckWinLimit(Archive &Arc,std::wstring &ArcFileName);
|
||||
|
||||
RarTime StartTime; // Time when extraction started.
|
||||
|
||||
CommandData *Cmd;
|
||||
|
||||
ComprDataIO DataIO;
|
||||
Unpack *Unp;
|
||||
unsigned long TotalFileCount;
|
||||
|
||||
unsigned long FileCount;
|
||||
unsigned long MatchedArgs;
|
||||
bool FirstFile;
|
||||
bool AllMatchesExact;
|
||||
bool ReconstructDone;
|
||||
bool UseExactVolName;
|
||||
|
||||
// If any non-zero solid file was successfully unpacked before current.
|
||||
// If true and if current encrypted file is broken, obviously
|
||||
// the password is correct and we can report broken CRC without
|
||||
// any wrong password hints.
|
||||
bool AnySolidDataUnpackedWell;
|
||||
|
||||
std::wstring ArcName;
|
||||
|
||||
bool GlobalPassword;
|
||||
bool PrevProcessed; // If previous file was successfully extracted or tested.
|
||||
std::wstring DestFileName;
|
||||
bool SuppressNoFilesMessage;
|
||||
|
||||
// In Windows it is set to true if at least one symlink with ".."
|
||||
// in target was extracted.
|
||||
bool ConvertSymlinkPaths;
|
||||
|
||||
// Last path checked for symlinks. We use it to improve the performance,
|
||||
// so we do not check recently checked folders again.
|
||||
std::wstring LastCheckedSymlink;
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
bool Fat32,NotFat32;
|
||||
#endif
|
||||
public:
|
||||
CmdExtract(CommandData *Cmd);
|
||||
~CmdExtract();
|
||||
void DoExtract();
|
||||
void ExtractArchiveInit(Archive &Arc);
|
||||
bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
|
||||
static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+150
@@ -0,0 +1,150 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
// If NewFile==NULL, we delete created file after user confirmation.
|
||||
// It is useful if we need to overwrite an existing folder or file,
|
||||
// but need user confirmation for that.
|
||||
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
|
||||
bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
|
||||
{
|
||||
if (UserReject!=NULL)
|
||||
*UserReject=false;
|
||||
#ifdef _WIN_ALL
|
||||
bool ShortNameChanged=false;
|
||||
#endif
|
||||
while (FileExist(Name))
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
if (!ShortNameChanged)
|
||||
{
|
||||
// Avoid the infinite loop if UpdateExistingShortName returns
|
||||
// the same name.
|
||||
ShortNameChanged=true;
|
||||
|
||||
// Maybe our long name matches the short name of existing file.
|
||||
// Let's check if we can change the short name.
|
||||
if (UpdateExistingShortName(Name))
|
||||
continue;
|
||||
}
|
||||
// Allow short name check again. It is necessary, because rename and
|
||||
// autorename below can change the name, so we need to check it again.
|
||||
ShortNameChanged=false;
|
||||
#endif
|
||||
UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
|
||||
|
||||
if (Choice==UIASKREP_R_REPLACE)
|
||||
break;
|
||||
if (Choice==UIASKREP_R_SKIP)
|
||||
{
|
||||
if (UserReject!=NULL)
|
||||
*UserReject=true;
|
||||
return false;
|
||||
}
|
||||
if (Choice==UIASKREP_R_CANCEL)
|
||||
ErrHandler.Exit(RARX_USERBREAK);
|
||||
}
|
||||
|
||||
// Try to truncate the existing file first instead of delete,
|
||||
// so we preserve existing file permissions, such as NTFS permissions,
|
||||
// also as "Compressed" attribute and hard links. In GUI version we avoid
|
||||
// deleting an existing file for non-.rar archive formats as well.
|
||||
uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
|
||||
if (NewFile!=NULL && NewFile->Create(Name,FileMode))
|
||||
return true;
|
||||
|
||||
CreatePath(Name,true,Cmd->DisableNames);
|
||||
return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN_ALL)
|
||||
// If we find a file, which short name is equal to 'Name', we try to change
|
||||
// its short name, while preserving the long name. It helps when unpacking
|
||||
// an archived file, which long name is equal to short name of already
|
||||
// existing file. Otherwise we would overwrite the already existing file,
|
||||
// even though its long name does not match the name of unpacking file.
|
||||
bool UpdateExistingShortName(const std::wstring &Name)
|
||||
{
|
||||
DWORD Res=GetLongPathName(Name.c_str(),NULL,0);
|
||||
if (Res==0)
|
||||
return false;
|
||||
std::vector<wchar> LongPathBuf(Res);
|
||||
Res=GetLongPathName(Name.c_str(),LongPathBuf.data(),(DWORD)LongPathBuf.size());
|
||||
if (Res==0 || Res>=LongPathBuf.size())
|
||||
return false;
|
||||
Res=GetShortPathName(Name.c_str(),NULL,0);
|
||||
if (Res==0)
|
||||
return false;
|
||||
std::vector<wchar> ShortPathBuf(Res);
|
||||
Res=GetShortPathName(Name.c_str(),ShortPathBuf.data(),(DWORD)ShortPathBuf.size());
|
||||
if (Res==0 || Res>=ShortPathBuf.size())
|
||||
return false;
|
||||
std::wstring LongPathName=LongPathBuf.data();
|
||||
std::wstring ShortPathName=ShortPathBuf.data();
|
||||
|
||||
std::wstring LongName=PointToName(LongPathName);
|
||||
std::wstring ShortName=PointToName(ShortPathName);
|
||||
|
||||
// We continue only if file has a short name, which does not match its
|
||||
// long name, and this short name is equal to name of file which we need
|
||||
// to create.
|
||||
if (ShortName.empty() || wcsicomp(LongName,ShortName)==0 ||
|
||||
wcsicomp(PointToName(Name),ShortName)!=0)
|
||||
return false;
|
||||
|
||||
// Generate the temporary new name for existing file.
|
||||
std::wstring NewName;
|
||||
for (uint I=0;I<10000 && NewName.empty();I+=123)
|
||||
{
|
||||
// Here we copy the path part of file to create. We'll make the temporary
|
||||
// file in the same folder.
|
||||
NewName=Name;
|
||||
|
||||
// Here we set the random name part.
|
||||
SetName(NewName,std::wstring(L"rtmp") + std::to_wstring(I));
|
||||
|
||||
// If such file is already exist, try next random name.
|
||||
if (FileExist(NewName))
|
||||
NewName.clear();
|
||||
}
|
||||
|
||||
// If we could not generate the name not used by any other file, we return.
|
||||
if (NewName.empty())
|
||||
return false;
|
||||
|
||||
// FastFind returns the name without path, but we need the fully qualified
|
||||
// name for renaming, so we use the path from file to create and long name
|
||||
// from existing file.
|
||||
std::wstring FullName=Name;
|
||||
SetName(FullName,LongName);
|
||||
|
||||
// Rename the existing file to randomly generated name. Normally it changes
|
||||
// the short name too.
|
||||
if (!MoveFile(FullName.c_str(),NewName.c_str()))
|
||||
return false;
|
||||
|
||||
// Now we need to create the temporary empty file with same name as
|
||||
// short name of our already existing file. We do it to occupy its previous
|
||||
// short name and not allow to use it again when renaming the file back to
|
||||
// its original long name.
|
||||
File KeepShortFile;
|
||||
bool Created=false;
|
||||
if (!FileExist(Name))
|
||||
Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
|
||||
|
||||
// Now we rename the existing file from temporary name to original long name.
|
||||
// Since its previous short name is occupied by another file, it should
|
||||
// get another short name.
|
||||
MoveFile(NewName.c_str(),FullName.c_str());
|
||||
|
||||
if (Created)
|
||||
{
|
||||
// Delete the temporary zero length file occupying the short name,
|
||||
KeepShortFile.Close();
|
||||
KeepShortFile.Delete();
|
||||
}
|
||||
// We successfully changed the short name. We do not use the simpler
|
||||
// SetFileShortName Windows API call, because it requires SE_RESTORE_NAME
|
||||
// privilege.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
#ifndef _RAR_FILECREATE_
|
||||
#define _RAR_FILECREATE_
|
||||
|
||||
bool FileCreate(CommandData *Cmd,File *NewFile,std::wstring &Name,
|
||||
bool *UserReject,int64 FileSize=INT64NDF,
|
||||
RarTime *FileTime=NULL,bool WriteOnly=false);
|
||||
|
||||
#if defined(_WIN_ALL)
|
||||
bool UpdateExistingShortName(const std::wstring &Name);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+855
@@ -0,0 +1,855 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
File::File()
|
||||
{
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
NewFile=false;
|
||||
LastWrite=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
LineInput=false;
|
||||
SkipClose=false;
|
||||
ErrorType=FILE_SUCCESS;
|
||||
OpenShared=false;
|
||||
AllowDelete=true;
|
||||
AllowExceptions=true;
|
||||
PreserveAtime=false;
|
||||
#ifdef _WIN_ALL
|
||||
// CreateMode=FMF_UNDEFINED;
|
||||
#endif
|
||||
ReadErrorMode=FREM_ASK;
|
||||
TruncatedAfterReadError=false;
|
||||
CurFilePos=0;
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
if (hFile!=FILE_BAD_HANDLE && !SkipClose)
|
||||
if (NewFile)
|
||||
Delete();
|
||||
else
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
void File::operator = (File &SrcFile)
|
||||
{
|
||||
hFile=SrcFile.hFile;
|
||||
NewFile=SrcFile.NewFile;
|
||||
LastWrite=SrcFile.LastWrite;
|
||||
HandleType=SrcFile.HandleType;
|
||||
TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
|
||||
FileName=SrcFile.FileName;
|
||||
SrcFile.SkipClose=true;
|
||||
}
|
||||
|
||||
|
||||
bool File::Open(const std::wstring &Name,uint Mode)
|
||||
{
|
||||
ErrorType=FILE_SUCCESS;
|
||||
FileHandle hNewFile;
|
||||
bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
|
||||
bool UpdateMode=(Mode & FMF_UPDATE)!=0;
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
#ifdef _WIN_ALL
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
|
||||
if (UpdateMode)
|
||||
Access|=GENERIC_WRITE;
|
||||
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
|
||||
if (OpenShared)
|
||||
ShareMode|=FILE_SHARE_WRITE;
|
||||
uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
FindData FD;
|
||||
if (PreserveAtime)
|
||||
Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
|
||||
hNewFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
DWORD LastError;
|
||||
if (hNewFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
LastError=GetLastError();
|
||||
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
{
|
||||
hNewFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
// For archive names longer than 260 characters first CreateFile
|
||||
// (without \\?\) fails and sets LastError to 3 (access denied).
|
||||
// We need the correct "file not found" error code to decide
|
||||
// if we create a new archive or quit with "cannot create" error.
|
||||
// So we need to check the error code after \\?\ CreateFile again,
|
||||
// otherwise we'll fail to create new archives with long names.
|
||||
// But we cannot simply assign the new code to LastError,
|
||||
// because it would break "..\arcname.rar" relative names processing.
|
||||
// First CreateFile returns the correct "file not found" code for such
|
||||
// names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
|
||||
// dots as a directory name. So we check only for "file not found"
|
||||
// error here and for other errors use the first CreateFile result.
|
||||
if (GetLastError()==ERROR_FILE_NOT_FOUND)
|
||||
LastError=ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
|
||||
{
|
||||
FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
|
||||
SetFileTime(hNewFile,NULL,&ft,NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
|
||||
|
||||
// 2025.06.09: We can't just set O_DIRECT for Unix like we set
|
||||
// FILE_FLAG_SEQUENTIAL_SCAN for Windows to minimize disk caching.
|
||||
// O_DIRECT might impose alignment requirements for data size, data address
|
||||
// and file offset. Also it might not be supported by some file systems
|
||||
// and fail with an error.
|
||||
#ifdef O_BINARY
|
||||
flags|=O_BINARY;
|
||||
#if defined(_AIX) && defined(_LARGE_FILE_API)
|
||||
flags|=O_LARGEFILE;
|
||||
#endif
|
||||
#endif
|
||||
// NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
|
||||
#if defined(O_NOATIME)
|
||||
if (PreserveAtime)
|
||||
flags|=O_NOATIME;
|
||||
#endif
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
|
||||
int handle=open(NameA.c_str(),flags);
|
||||
#ifdef LOCK_EX
|
||||
|
||||
#ifdef _OSF_SOURCE
|
||||
extern "C" int flock(int, int);
|
||||
#endif
|
||||
if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
|
||||
{
|
||||
close(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (handle==-1)
|
||||
hNewFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hNewFile=handle;
|
||||
#else
|
||||
hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
|
||||
#endif
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
#endif
|
||||
NewFile=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
bool Success=hNewFile!=FILE_BAD_HANDLE;
|
||||
if (Success)
|
||||
{
|
||||
hFile=hNewFile;
|
||||
FileName=Name;
|
||||
TruncatedAfterReadError=false;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TOpen(const std::wstring &Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
ErrHandler.Exit(RARX_OPEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WOpen(const std::wstring &Name)
|
||||
{
|
||||
if (Open(Name))
|
||||
return true;
|
||||
ErrHandler.OpenErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Create(const std::wstring &Name,uint Mode)
|
||||
{
|
||||
// 2025.09.03: Likely outdated info, see https://www.illumos.org/issues/2000
|
||||
// OpenIndiana based NAS and CIFS shares fail to set the file time if file
|
||||
// was created in read+write mode and some data was written and not flushed
|
||||
// before SetFileTime call. So we should use the write only mode if we plan
|
||||
// SetFileTime call and do not need to read from file.
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
|
||||
#ifdef _WIN_ALL
|
||||
// CreateMode=Mode;
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
|
||||
DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
|
||||
|
||||
// Windows automatically removes dots and spaces in the end of file name,
|
||||
// So we detect such names and process them with \\?\ prefix.
|
||||
wchar LastChar=GetLastChar(Name);
|
||||
bool Special=LastChar=='.' || LastChar==' ';
|
||||
|
||||
if (Special && (Mode & FMF_STANDARDNAMES)==0)
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
hFile=CreateFile(Name.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
hFile=CreateFile(LongName.c_str(),Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
hFile=open(NameA.c_str(),(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
|
||||
#else
|
||||
hFile=fopen(NameA.c_str(),WriteMode ? WRITEBINARY:CREATEBINARY);
|
||||
#endif
|
||||
#endif
|
||||
NewFile=true;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
FileName=Name;
|
||||
return hFile!=FILE_BAD_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TCreate(const std::wstring &Name,uint Mode)
|
||||
{
|
||||
if (!WCreate(Name,Mode))
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WCreate(const std::wstring &Name,uint Mode)
|
||||
{
|
||||
if (Create(Name,Mode))
|
||||
return true;
|
||||
ErrHandler.CreateErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Close()
|
||||
{
|
||||
bool Success=true;
|
||||
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
{
|
||||
if (!SkipClose)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We use the standard system handle for stdout in Windows
|
||||
// and it must not be closed here.
|
||||
if (HandleType==FILE_HANDLENORMAL)
|
||||
Success=CloseHandle(hFile)!=FALSE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
Success=close(hFile)!=-1;
|
||||
#else
|
||||
Success=fclose(hFile)!=EOF;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
}
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
if (!Success && AllowExceptions)
|
||||
ErrHandler.CloseError(FileName);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Delete()
|
||||
{
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
return false;
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
Close();
|
||||
if (!AllowDelete)
|
||||
return false;
|
||||
return DelFile(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::Rename(const std::wstring &NewName)
|
||||
{
|
||||
// No need to rename if names are already same.
|
||||
bool Success=(NewName==FileName);
|
||||
|
||||
if (!Success)
|
||||
Success=RenameFile(FileName,NewName);
|
||||
|
||||
if (Success)
|
||||
FileName=NewName;
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Write(const void *Data,size_t Size)
|
||||
{
|
||||
if (Size==0)
|
||||
return true;
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
hFile=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#else
|
||||
// Cannot use the standard stdout here, because it already has wide orientation.
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=dup(STDOUT_FILENO); // Open new stdout stream.
|
||||
#else
|
||||
hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool Success;
|
||||
while (1)
|
||||
{
|
||||
Success=false;
|
||||
#ifdef _WIN_ALL
|
||||
DWORD Written=0;
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
{
|
||||
// writing to stdout can fail in old Windows if data block is too large
|
||||
const size_t MaxSize=0x4000;
|
||||
for (size_t I=0;I<Size;I+=MaxSize)
|
||||
{
|
||||
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)!=FALSE;
|
||||
if (!Success)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)!=FALSE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t Written=write(hFile,Data,Size);
|
||||
Success=Written==Size;
|
||||
#else
|
||||
int Written=fwrite(Data,1,Size,hFile);
|
||||
Success=Written==Size && !ferror(hFile);
|
||||
#endif
|
||||
#endif
|
||||
if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
|
||||
{
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
int ErrCode=GetLastError();
|
||||
int64 FilePos=Tell();
|
||||
uint64 FreeSize=GetFreeDisk(FileName);
|
||||
SetLastError(ErrCode);
|
||||
if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
|
||||
ErrHandler.WriteErrorFAT(FileName);
|
||||
#endif
|
||||
if (ErrHandler.AskRepeatWrite(FileName,false))
|
||||
{
|
||||
#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
|
||||
clearerr(hFile);
|
||||
#endif
|
||||
if (Written<Size && Written>0)
|
||||
Seek(Tell()-Written,SEEK_SET);
|
||||
continue;
|
||||
}
|
||||
ErrHandler.WriteError(L"",FileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LastWrite=true;
|
||||
return Success; // It can return false only if AllowExceptions is disabled.
|
||||
}
|
||||
|
||||
|
||||
int File::Read(void *Data,size_t Size)
|
||||
{
|
||||
if (TruncatedAfterReadError)
|
||||
return 0;
|
||||
|
||||
int64 FilePos=0; // Initialized only to suppress some compilers warning.
|
||||
|
||||
if (ReadErrorMode==FREM_IGNORE)
|
||||
FilePos=Tell();
|
||||
int TotalRead=0;
|
||||
while (true)
|
||||
{
|
||||
int ReadSize=DirectRead(Data,Size);
|
||||
|
||||
if (ReadSize==-1)
|
||||
{
|
||||
ErrorType=FILE_READERROR;
|
||||
if (AllowExceptions)
|
||||
if (ReadErrorMode==FREM_IGNORE)
|
||||
{
|
||||
ReadSize=0;
|
||||
for (size_t I=0;I<Size;I+=512)
|
||||
{
|
||||
Seek(FilePos+I,SEEK_SET);
|
||||
size_t SizeToRead=Min(Size-I,512);
|
||||
int ReadCode=DirectRead(Data,SizeToRead);
|
||||
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
|
||||
if (ReadSize!=-1)
|
||||
TotalRead+=ReadSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool Ignore=false,Retry=false,Quit=false;
|
||||
if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL && IsOpened())
|
||||
{
|
||||
ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
|
||||
if (Retry)
|
||||
continue;
|
||||
}
|
||||
if (Ignore || ReadErrorMode==FREM_TRUNCATE)
|
||||
{
|
||||
TruncatedAfterReadError=true;
|
||||
return 0;
|
||||
}
|
||||
ErrHandler.ReadError(FileName);
|
||||
}
|
||||
}
|
||||
TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here.
|
||||
|
||||
if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size)
|
||||
{
|
||||
// Unlike regular files, for pipe we can read only as much as was
|
||||
// written at the other end of pipe. We had seen data coming in small
|
||||
// ~80 byte chunks when piping from 'type arc.rar'. Extraction code
|
||||
// would fail if we read an incomplete archive header from stdin.
|
||||
// So here we ensure that requested size is completely read.
|
||||
// But we return the available data immediately in "line input" mode,
|
||||
// when processing user's input in console prompts. Otherwise apps
|
||||
// piping user responses to multiple Ask() prompts can hang if no more
|
||||
// data is available yet and pipe isn't closed.
|
||||
Data=(byte*)Data+ReadSize;
|
||||
Size-=ReadSize;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled.
|
||||
CurFilePos+=TotalRead;
|
||||
return TotalRead; // It can return -1 only if AllowExceptions is disabled.
|
||||
}
|
||||
|
||||
|
||||
// Returns -1 in case of error.
|
||||
int File::DirectRead(void *Data,size_t Size)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
const size_t MaxDeviceRead=20000;
|
||||
const size_t MaxLockedRead=32768;
|
||||
#endif
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// if (Size>MaxDeviceRead)
|
||||
// Size=MaxDeviceRead;
|
||||
hFile=GetStdHandle(STD_INPUT_HANDLE);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=STDIN_FILENO;
|
||||
#else
|
||||
hFile=stdin;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
// For pipes like 'type file.txt | rar -si arcname' ReadFile may return
|
||||
// data in small ~4KB blocks. It may slightly reduce the compression ratio.
|
||||
DWORD Read;
|
||||
if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
|
||||
{
|
||||
if (IsDevice() && Size>MaxDeviceRead)
|
||||
return DirectRead(Data,MaxDeviceRead);
|
||||
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
|
||||
return 0;
|
||||
|
||||
// We had a bug report about failure to archive 1C database lock file
|
||||
// 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
|
||||
// permanently locked. If our first read request uses too large buffer
|
||||
// and if we are in -dh mode, so we were able to open the file,
|
||||
// we'll fail with "Read error". So now we use try a smaller buffer size
|
||||
// in case of lock error.
|
||||
if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
|
||||
GetLastError()==ERROR_LOCK_VIOLATION)
|
||||
return DirectRead(Data,MaxLockedRead);
|
||||
|
||||
return -1;
|
||||
}
|
||||
return Read;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t ReadSize=read(hFile,Data,Size);
|
||||
if (ReadSize==-1)
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#else
|
||||
if (LastWrite)
|
||||
{
|
||||
fflush(hFile);
|
||||
LastWrite=false;
|
||||
}
|
||||
clearerr(hFile);
|
||||
size_t ReadSize=fread(Data,1,Size,hFile);
|
||||
if (ferror(hFile))
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!RawSeek(Offset,Method) && AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::RawSeek(int64 Offset,int Method)
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return true;
|
||||
if (!IsSeekable()) // To extract archives from stdin with -si.
|
||||
{
|
||||
// We tried to dynamically allocate 32 KB buffer here, but it improved
|
||||
// speed in Windows 10 by mere ~1.5%.
|
||||
byte Buf[4096];
|
||||
if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos)
|
||||
{
|
||||
uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos;
|
||||
while (SkipSize>0) // Reading to emulate seek forward.
|
||||
{
|
||||
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
|
||||
if (ReadSize<=0)
|
||||
return false;
|
||||
SkipSize-=ReadSize;
|
||||
CurFilePos+=ReadSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking
|
||||
// RAR 4.x archives without the end of archive block created with -en.
|
||||
if (Method==SEEK_END)
|
||||
{
|
||||
int ReadSize;
|
||||
while ((ReadSize=Read(Buf,ASIZE(Buf)))>0)
|
||||
CurFilePos+=ReadSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Backward seek on unseekable file.
|
||||
}
|
||||
if (Offset<0 && Method!=SEEK_SET)
|
||||
{
|
||||
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
|
||||
Method=SEEK_SET;
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=(LONG)(Offset>>32);
|
||||
if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
|
||||
GetLastError()!=NO_ERROR)
|
||||
return false;
|
||||
#else
|
||||
LastWrite=false;
|
||||
#ifdef FILE_USE_OPEN
|
||||
if (lseek(hFile,(off_t)Offset,Method)==-1)
|
||||
return false;
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
|
||||
if (fseeko(hFile,Offset,Method)!=0)
|
||||
return false;
|
||||
#else
|
||||
if (fseek(hFile,(long)Offset,Method)!=0)
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int64 File::Tell()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
if (!IsSeekable())
|
||||
return CurFilePos;
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=0;
|
||||
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
|
||||
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
return INT32TO64(HighDist,LowDist);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
return lseek(hFile,0,SEEK_CUR);
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
|
||||
return ftello(hFile);
|
||||
#else
|
||||
return ftell(hFile);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Prealloc(int64 Size)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if (RawSeek(Size,SEEK_SET))
|
||||
{
|
||||
Truncate();
|
||||
Seek(0,SEEK_SET);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_UNIX) && defined(USE_FALLOCATE)
|
||||
// fallocate is rather new call. Only latest kernels support it.
|
||||
// So we are not using it by default yet.
|
||||
int fd = GetFD();
|
||||
if (fd >= 0)
|
||||
fallocate(fd, 0, 0, Size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
byte File::GetByte()
|
||||
{
|
||||
byte Byte=0;
|
||||
Read(&Byte,1);
|
||||
return Byte;
|
||||
}
|
||||
|
||||
|
||||
void File::PutByte(byte Byte)
|
||||
{
|
||||
Write(&Byte,1);
|
||||
}
|
||||
|
||||
|
||||
bool File::Truncate()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return SetEndOfFile(hFile)!=FALSE;
|
||||
#else
|
||||
return ftruncate(GetFD(),(off_t)Tell())==0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Flush()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
FlushFileBuffers(hFile);
|
||||
#else
|
||||
#ifndef FILE_USE_OPEN
|
||||
fflush(hFile);
|
||||
#endif
|
||||
fsync(GetFD());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// Workaround for OpenIndiana NAS time bug. If we cannot create a file
|
||||
// in write only mode, we need to flush the write buffer before calling
|
||||
// SetFileTime or file time will not be changed.
|
||||
// 2025.09.03: Removed this code as likely redundant now,
|
||||
// see https://www.illumos.org/issues/2000
|
||||
// if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
|
||||
// FlushFileBuffers(hFile);
|
||||
|
||||
bool sm=ftm!=NULL && ftm->IsSet();
|
||||
bool sc=ftc!=NULL && ftc->IsSet();
|
||||
bool sa=fta!=NULL && fta->IsSet();
|
||||
FILETIME fm,fc,fa;
|
||||
if (sm)
|
||||
ftm->GetWinFT(&fm);
|
||||
if (sc)
|
||||
ftc->GetWinFT(&fc);
|
||||
if (sa)
|
||||
fta->GetWinFT(&fa);
|
||||
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
// Android APP_PLATFORM := android-14 does not support futimens and futimes.
|
||||
// Newer platforms support futimens, but fail on Android 4.2.
|
||||
// We have to use utime for Android.
|
||||
// Also we noticed futimens fail to set timestamps on NTFS partition
|
||||
// mounted to virtual Linux x86 machine, but utimensat worked correctly.
|
||||
// So we set timestamps for already closed files in Unix.
|
||||
#ifdef _UNIX
|
||||
SetCloseFileTimeByName(FileName,ftm,fta);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
bool setm=ftm!=NULL && ftm->IsSet();
|
||||
bool seta=fta!=NULL && fta->IsSet();
|
||||
if (setm || seta)
|
||||
{
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
|
||||
#ifdef UNIX_TIME_NS
|
||||
timespec times[2];
|
||||
times[0].tv_sec=seta ? fta->GetUnix() : 0;
|
||||
times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
times[1].tv_sec=setm ? ftm->GetUnix() : 0;
|
||||
times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
utimensat(AT_FDCWD,NameA.c_str(),times,0);
|
||||
#else
|
||||
utimbuf ut;
|
||||
if (setm)
|
||||
ut.modtime=ftm->GetUnix();
|
||||
else
|
||||
ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
|
||||
if (seta)
|
||||
ut.actime=fta->GetUnix();
|
||||
else
|
||||
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
|
||||
utime(NameA.c_str(),&ut);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef _UNIX
|
||||
void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#ifdef UNIX_TIME_NS
|
||||
#if defined(_APPLE)
|
||||
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec);
|
||||
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec);
|
||||
if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec);
|
||||
#else
|
||||
if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
|
||||
if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
|
||||
if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
|
||||
#endif
|
||||
#else
|
||||
if (ftm!=NULL) ftm->SetUnix(st.st_mtime);
|
||||
if (ftc!=NULL) ftc->SetUnix(st.st_ctime);
|
||||
if (fta!=NULL) fta->SetUnix(st.st_atime);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
FILETIME ctime,atime,mtime;
|
||||
GetFileTime(hFile,&ctime,&atime,&mtime);
|
||||
if (ftm!=NULL) ftm->SetWinFT(&mtime);
|
||||
if (ftc!=NULL) ftc->SetWinFT(&ctime);
|
||||
if (fta!=NULL) fta->SetWinFT(&atime);
|
||||
#elif defined(_UNIX)
|
||||
struct stat st;
|
||||
fstat(GetFD(),&st);
|
||||
StatToRarTime(st,ftm,ftc,fta);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int64 File::FileLength()
|
||||
{
|
||||
int64 SavePos=Tell();
|
||||
Seek(0,SEEK_END);
|
||||
int64 Length=Tell();
|
||||
Seek(SavePos,SEEK_SET);
|
||||
return Length;
|
||||
}
|
||||
|
||||
|
||||
bool File::IsDevice()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return false;
|
||||
#ifdef _WIN_ALL
|
||||
uint Type=GetFileType(hFile);
|
||||
return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
|
||||
#else
|
||||
return isatty(GetFD());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 File::Copy(File &Dest,int64 Length)
|
||||
{
|
||||
bool CopyAll=(Length==INT64NDF);
|
||||
|
||||
// Adjust the buffer size to data size. So we do not waste too much time
|
||||
// to vector initialization when copying many small data blocks like
|
||||
// when updating an archive with many small files.
|
||||
size_t BufSize=File::CopyBufferSize();
|
||||
if (!CopyAll && Length<(int64)BufSize)
|
||||
BufSize=(size_t)Length;
|
||||
|
||||
std::vector<byte> Buffer(BufSize);
|
||||
int64 CopySize=0;
|
||||
|
||||
while (CopyAll || Length>0)
|
||||
{
|
||||
Wait();
|
||||
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.size()) ? (size_t)Length:Buffer.size();
|
||||
byte *Buf=Buffer.data();
|
||||
int ReadSize=Read(Buf,SizeToRead);
|
||||
if (ReadSize==0)
|
||||
break;
|
||||
size_t WriteSize=ReadSize;
|
||||
#ifdef _WIN_ALL
|
||||
// For FAT32 USB flash drives in Windows if first write is 4 KB or more,
|
||||
// write caching is disabled and "write through" is enabled, resulting
|
||||
// in bad performance, especially for many small files. It happens when
|
||||
// we create SFX archive on USB drive, because SFX module is written first.
|
||||
// So we split the first write to small 1 KB followed by rest of data.
|
||||
if (CopySize==0 && WriteSize>=4096)
|
||||
{
|
||||
const size_t FirstWrite=1024;
|
||||
Dest.Write(Buf,FirstWrite);
|
||||
Buf+=FirstWrite;
|
||||
WriteSize-=FirstWrite;
|
||||
}
|
||||
#endif
|
||||
Dest.Write(Buf,WriteSize);
|
||||
CopySize+=ReadSize;
|
||||
if (!CopyAll)
|
||||
Length-=ReadSize;
|
||||
}
|
||||
return CopySize;
|
||||
}
|
||||
#endif
|
||||
Vendored
+156
@@ -0,0 +1,156 @@
|
||||
#ifndef _RAR_FILE_
|
||||
#define _RAR_FILE_
|
||||
|
||||
#define FILE_USE_OPEN
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
typedef HANDLE FileHandle;
|
||||
#define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
|
||||
#elif defined(FILE_USE_OPEN)
|
||||
typedef off_t FileHandle;
|
||||
#define FILE_BAD_HANDLE -1
|
||||
#else
|
||||
typedef FILE* FileHandle;
|
||||
#define FILE_BAD_HANDLE NULL
|
||||
#endif
|
||||
|
||||
enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
|
||||
|
||||
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
|
||||
|
||||
enum FILE_MODE_FLAGS {
|
||||
// Request read only access to file. Default for Open.
|
||||
FMF_READ=0,
|
||||
|
||||
// Request both read and write access to file. Default for Create.
|
||||
FMF_UPDATE=1,
|
||||
|
||||
// Request write only access to file.
|
||||
FMF_WRITE=2,
|
||||
|
||||
// Open files which are already opened for write by other programs.
|
||||
FMF_OPENSHARED=4,
|
||||
|
||||
// Open files only if no other program is opened it even in shared mode.
|
||||
FMF_OPENEXCLUSIVE=8,
|
||||
|
||||
// Provide read access to created file for other programs.
|
||||
FMF_SHAREREAD=16,
|
||||
|
||||
// Use standard NTFS names without trailing dots and spaces.
|
||||
FMF_STANDARDNAMES=32,
|
||||
|
||||
// Mode flags are not defined yet.
|
||||
// FMF_UNDEFINED=256
|
||||
};
|
||||
|
||||
enum FILE_READ_ERROR_MODE {
|
||||
FREM_ASK, // Propose to use the already read part, retry or abort.
|
||||
FREM_TRUNCATE, // Use the already read part without additional prompt.
|
||||
FREM_IGNORE // Try to skip unreadable block and read further.
|
||||
};
|
||||
|
||||
|
||||
class File
|
||||
{
|
||||
private:
|
||||
FileHandle hFile;
|
||||
bool LastWrite;
|
||||
FILE_HANDLETYPE HandleType;
|
||||
|
||||
// If we read the user input in console prompts from stdin, we shall
|
||||
// process the available line immediately, not waiting for rest of data.
|
||||
// Otherwise apps piping user responses to multiple Ask() prompts can
|
||||
// hang if no more data is available yet and pipe isn't closed.
|
||||
// If we read RAR archive or other file data from stdin, we shall collect
|
||||
// the entire requested block as long as pipe isn't closed, so we get
|
||||
// complete archive headers, not split between different reads.
|
||||
bool LineInput;
|
||||
|
||||
bool SkipClose;
|
||||
FILE_READ_ERROR_MODE ReadErrorMode;
|
||||
bool NewFile;
|
||||
bool AllowDelete;
|
||||
bool AllowExceptions;
|
||||
#ifdef _WIN_ALL
|
||||
// uint CreateMode;
|
||||
#endif
|
||||
bool PreserveAtime;
|
||||
bool TruncatedAfterReadError;
|
||||
|
||||
int64 CurFilePos; // Used for forward seeks in stdin files.
|
||||
protected:
|
||||
bool OpenShared; // Set by 'Archive' class.
|
||||
public:
|
||||
std::wstring FileName;
|
||||
|
||||
FILE_ERRORTYPE ErrorType;
|
||||
public:
|
||||
File();
|
||||
virtual ~File();
|
||||
void operator = (File &SrcFile);
|
||||
|
||||
// Several functions below are 'virtual', because they are redefined
|
||||
// by Archive for QOpen and by MultiFile for split files in WinRAR.
|
||||
virtual bool Open(const std::wstring &Name,uint Mode=FMF_READ);
|
||||
void TOpen(const std::wstring &Name);
|
||||
bool WOpen(const std::wstring &Name);
|
||||
bool Create(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
void TCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
bool WCreate(const std::wstring &Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
|
||||
virtual bool Close(); // 'virtual' for MultiFile class.
|
||||
bool Delete();
|
||||
bool Rename(const std::wstring &NewName);
|
||||
bool Write(const void *Data,size_t Size);
|
||||
virtual int Read(void *Data,size_t Size);
|
||||
int DirectRead(void *Data,size_t Size);
|
||||
virtual void Seek(int64 Offset,int Method);
|
||||
bool RawSeek(int64 Offset,int Method);
|
||||
virtual int64 Tell();
|
||||
void Prealloc(int64 Size);
|
||||
byte GetByte();
|
||||
void PutByte(byte Byte);
|
||||
bool Truncate();
|
||||
void Flush();
|
||||
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
|
||||
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
|
||||
static void SetCloseFileTimeByName(const std::wstring &Name,RarTime *ftm,RarTime *fta);
|
||||
#ifdef _UNIX
|
||||
static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta);
|
||||
#endif
|
||||
void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
|
||||
virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
|
||||
virtual int64 FileLength(); // 'virtual' for MultiFile class.
|
||||
void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
|
||||
void SetLineInputMode(bool Mode) {LineInput=Mode;}
|
||||
FILE_HANDLETYPE GetHandleType() {return HandleType;}
|
||||
bool IsSeekable() {return HandleType!=FILE_HANDLESTD;}
|
||||
bool IsDevice();
|
||||
static bool RemoveCreated();
|
||||
FileHandle GetHandle() {return hFile;}
|
||||
void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
|
||||
void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;}
|
||||
int64 Copy(File &Dest,int64 Length=INT64NDF);
|
||||
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
|
||||
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
|
||||
void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
|
||||
bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;}
|
||||
#ifdef _UNIX
|
||||
int GetFD()
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
return hFile;
|
||||
#else
|
||||
return fileno(hFile);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
static size_t CopyBufferSize()
|
||||
{
|
||||
// Values in 0x100000 - 0x400000 range are ok, but multithreaded CRC32
|
||||
// seems to benefit from 0x400000, especially on ARM CPUs.
|
||||
return 0x400000;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+630
@@ -0,0 +1,630 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// Windows automatically removes dots and spaces in the end of directory
|
||||
// name. So we detect such names and process them with \\?\ prefix.
|
||||
wchar LastChar=GetLastChar(Name);
|
||||
bool Special=LastChar=='.' || LastChar==' ';
|
||||
BOOL RetCode=Special ? FALSE : CreateDirectory(Name.c_str(),NULL);
|
||||
if (RetCode==0 && !FileExist(Name))
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
RetCode=CreateDirectory(LongName.c_str(),NULL);
|
||||
}
|
||||
if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
|
||||
{
|
||||
if (SetAttr)
|
||||
SetFileAttr(Name,Attr);
|
||||
return MKDIR_SUCCESS;
|
||||
}
|
||||
int ErrCode=GetLastError();
|
||||
if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
|
||||
return MKDIR_BADPATH;
|
||||
return MKDIR_ERROR;
|
||||
#elif defined(_UNIX)
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
|
||||
int ErrCode=mkdir(NameA.c_str(),uattr);
|
||||
if (ErrCode==-1)
|
||||
return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
|
||||
return MKDIR_SUCCESS;
|
||||
#else
|
||||
return MKDIR_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Simplified version of MakeDir().
|
||||
bool CreateDir(const std::wstring &Name)
|
||||
{
|
||||
return MakeDir(Name,false,0)==MKDIR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent)
|
||||
{
|
||||
if (Path.empty())
|
||||
return false;
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
uint DirAttr=0;
|
||||
#else
|
||||
uint DirAttr=0777;
|
||||
#endif
|
||||
|
||||
bool Success=true;
|
||||
|
||||
for (size_t I=0;I<Path.size();I++)
|
||||
{
|
||||
// Process all kinds of path separators, so user can enter Unix style
|
||||
// path in Windows or Windows in Unix. I>0 check avoids attempting
|
||||
// creating an empty directory for paths starting from path separator.
|
||||
if (IsPathDiv(Path[I]) && I>0)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We must not attempt to create "D:" directory, because first
|
||||
// CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
|
||||
// to create "D:" directory.
|
||||
if (I==2 && Path[1]==':')
|
||||
continue;
|
||||
#endif
|
||||
std::wstring DirName=Path.substr(0,I);
|
||||
Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
|
||||
if (Success && !Silent)
|
||||
{
|
||||
mprintf(St(MCreatDir),DirName.c_str());
|
||||
mprintf(L" %s",St(MOk));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!SkipLastName && !IsPathDiv(GetLastChar(Path)))
|
||||
Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
bool sm=ftm!=NULL && ftm->IsSet();
|
||||
bool sc=ftc!=NULL && ftc->IsSet();
|
||||
bool sa=fta!=NULL && fta->IsSet();
|
||||
|
||||
uint DirAttr=GetFileAttr(Name);
|
||||
bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
|
||||
if (ResetAttr)
|
||||
SetFileAttr(Name,0);
|
||||
|
||||
HANDLE hFile=CreateFile(Name.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
hFile=CreateFile(LongName.c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
|
||||
}
|
||||
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
FILETIME fm,fc,fa;
|
||||
if (sm)
|
||||
ftm->GetWinFT(&fm);
|
||||
if (sc)
|
||||
ftc->GetWinFT(&fc);
|
||||
if (sa)
|
||||
fta->GetWinFT(&fa);
|
||||
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
|
||||
CloseHandle(hFile);
|
||||
if (ResetAttr)
|
||||
SetFileAttr(Name,DirAttr);
|
||||
#endif
|
||||
#ifdef _UNIX
|
||||
File::SetCloseFileTimeByName(Name,ftm,fta);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsRemovable(const std::wstring &Name)
|
||||
{
|
||||
#if defined(_WIN_ALL)
|
||||
std::wstring Root;
|
||||
GetPathRoot(Name,Root);
|
||||
int Type=GetDriveType(Root.empty() ? nullptr : Root.c_str());
|
||||
return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 GetFreeDisk(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
std::wstring Root;
|
||||
GetPathWithSep(Name,Root);
|
||||
|
||||
ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
|
||||
uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
|
||||
if (GetDiskFreeSpaceEx(Root.empty() ? NULL:Root.c_str(),&uiUserFree,&uiTotalSize,&uiTotalFree) &&
|
||||
uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
|
||||
return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
|
||||
return 0;
|
||||
#elif defined(_UNIX)
|
||||
std::wstring Root;
|
||||
GetPathWithSep(Name,Root);
|
||||
std::string RootA;
|
||||
WideToChar(Root,RootA);
|
||||
struct statvfs sfs;
|
||||
if (statvfs(RootA.empty() ? ".":RootA.c_str(),&sfs)!=0)
|
||||
return 0;
|
||||
int64 FreeSize=sfs.f_bsize;
|
||||
FreeSize=FreeSize*sfs.f_bavail;
|
||||
return FreeSize;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
|
||||
// file size to 4 GB for these file systems.
|
||||
bool IsFAT(const std::wstring &Name)
|
||||
{
|
||||
std::wstring Root;
|
||||
GetPathRoot(Name,Root);
|
||||
wchar FileSystem[MAX_PATH+1];
|
||||
// Root can be empty, when we create volumes with -v in the current folder.
|
||||
if (GetVolumeInformation(Root.empty() ? NULL:Root.c_str(),NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
|
||||
return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool FileExist(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return GetFileAttr(Name)!=0xffffffff;
|
||||
#elif defined(ENABLE_ACCESS)
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
return access(NameA.c_str(),0)==0;
|
||||
#else
|
||||
FindData FD;
|
||||
return FindFile::FastFind(Name,&FD);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool WildFileExist(const std::wstring &Name)
|
||||
{
|
||||
if (IsWildcard(Name))
|
||||
{
|
||||
FindFile Find;
|
||||
Find.SetMask(Name);
|
||||
FindData fd;
|
||||
return Find.Next(&fd);
|
||||
}
|
||||
return FileExist(Name);
|
||||
}
|
||||
|
||||
|
||||
bool IsDir(uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
|
||||
#endif
|
||||
#if defined(_UNIX)
|
||||
return (Attr & 0xF000)==0x4000;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool IsUnreadable(uint Attr)
|
||||
{
|
||||
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
|
||||
return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool IsLink(uint Attr)
|
||||
{
|
||||
#ifdef _UNIX
|
||||
return (Attr & 0xF000)==0xA000;
|
||||
#elif defined(_WIN_ALL)
|
||||
return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsDeleteAllowed(uint FileAttr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
|
||||
#else
|
||||
return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PrepareToDelete(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
SetFileAttr(Name,0);
|
||||
#endif
|
||||
#ifdef _UNIX
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
chmod(NameA.c_str(),S_IRUSR|S_IWUSR|S_IXUSR);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint GetFileAttr(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
DWORD Attr=GetFileAttributes(Name.c_str());
|
||||
if (Attr==0xffffffff)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
Attr=GetFileAttributes(LongName.c_str());
|
||||
}
|
||||
return Attr;
|
||||
#else
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
struct stat st;
|
||||
if (stat(NameA.c_str(),&st)!=0)
|
||||
return 0;
|
||||
return st.st_mode;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool SetFileAttr(const std::wstring &Name,uint Attr)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=SetFileAttributes(Name.c_str(),Attr)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
Success=SetFileAttributes(LongName.c_str(),Attr)!=0;
|
||||
}
|
||||
return Success;
|
||||
#elif defined(_UNIX)
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
return chmod(NameA.c_str(),(mode_t)Attr)==0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Ext is the extension with the leading dot, like L".bat", or nullptr to use
|
||||
// the default extension.
|
||||
bool MkTemp(std::wstring &Name,const wchar *Ext)
|
||||
{
|
||||
RarTime CurTime;
|
||||
CurTime.SetCurrentTime();
|
||||
|
||||
// We cannot use CurTime.GetWin() as is, because its lowest bits can
|
||||
// have low informational value, like being a zero or few fixed numbers.
|
||||
uint Random=(uint)(CurTime.GetWin()/100000);
|
||||
|
||||
// Using PID we guarantee that different RAR copies use different temp names
|
||||
// even if started in exactly the same time.
|
||||
uint PID=0;
|
||||
#ifdef _WIN_ALL
|
||||
PID=(uint)GetCurrentProcessId();
|
||||
#elif defined(_UNIX)
|
||||
PID=(uint)getpid();
|
||||
#endif
|
||||
|
||||
for (uint Attempt=0;;Attempt++)
|
||||
{
|
||||
uint RandomExt=Random%50000+Attempt;
|
||||
if (Attempt==1000)
|
||||
return false;
|
||||
|
||||
// User asked to specify the single extension for all temporary files,
|
||||
// so it can be added to server ransomware protection exceptions.
|
||||
// He wrote, this protection blocks temporary files when adding
|
||||
// a file to RAR archive with drag and drop. So unless a calling code
|
||||
// requires a specific extension, like .bat file when uninstalling,
|
||||
// we set the uniform extension here.
|
||||
if (Ext==nullptr)
|
||||
Ext=L".rartemp";
|
||||
|
||||
std::wstring NewName=Name + std::to_wstring(PID) + L"." + std::to_wstring(RandomExt) + Ext;
|
||||
if (!FileExist(NewName))
|
||||
{
|
||||
Name=NewName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
|
||||
{
|
||||
int64 SavePos=SrcFile->Tell();
|
||||
#ifndef SILENT
|
||||
int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
|
||||
#endif
|
||||
|
||||
if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
|
||||
uiMsg(UIEVENT_FILESUMSTART);
|
||||
|
||||
if ((Flags & CALCFSUM_CURPOS)==0)
|
||||
SrcFile->Seek(0,SEEK_SET);
|
||||
|
||||
const size_t BufSize=0x100000;
|
||||
std::vector<byte> Data(BufSize);
|
||||
|
||||
DataHash HashCRC,HashBlake2;
|
||||
HashCRC.Init(HASH_CRC32,Threads);
|
||||
HashBlake2.Init(HASH_BLAKE2,Threads);
|
||||
|
||||
int64 BlockCount=0;
|
||||
int64 TotalRead=0;
|
||||
while (true)
|
||||
{
|
||||
size_t SizeToRead;
|
||||
if (Size==INT64NDF) // If we process the entire file.
|
||||
SizeToRead=BufSize; // Then always attempt to read the entire buffer.
|
||||
else
|
||||
SizeToRead=(size_t)Min((int64)BufSize,Size);
|
||||
int ReadSize=SrcFile->Read(Data.data(),SizeToRead);
|
||||
if (ReadSize==0)
|
||||
break;
|
||||
TotalRead+=ReadSize;
|
||||
|
||||
if ((++BlockCount & 0xf)==0)
|
||||
{
|
||||
#ifndef SILENT
|
||||
if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
|
||||
{
|
||||
// Update only the current file progress in WinRAR, set the total to 0
|
||||
// to keep it as is. It looks better for WinRAR.
|
||||
uiExtractProgress(TotalRead,FileLength,0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
|
||||
uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
|
||||
}
|
||||
#endif
|
||||
Wait();
|
||||
}
|
||||
|
||||
if (CRC32!=NULL)
|
||||
HashCRC.Update(Data.data(),ReadSize);
|
||||
if (Blake2!=NULL)
|
||||
HashBlake2.Update(Data.data(),ReadSize);
|
||||
|
||||
if (Size!=INT64NDF)
|
||||
Size-=ReadSize;
|
||||
}
|
||||
SrcFile->Seek(SavePos,SEEK_SET);
|
||||
|
||||
if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
|
||||
uiMsg(UIEVENT_FILESUMEND);
|
||||
|
||||
if (CRC32!=NULL)
|
||||
*CRC32=HashCRC.GetCRC32();
|
||||
if (Blake2!=NULL)
|
||||
{
|
||||
HashValue Result;
|
||||
HashBlake2.Result(&Result);
|
||||
memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=MoveFile(SrcName.c_str(),DestName.c_str())!=0;
|
||||
if (!Success)
|
||||
{
|
||||
std::wstring LongName1,LongName2;
|
||||
if (GetWinLongPath(SrcName,LongName1) && GetWinLongPath(DestName,LongName2))
|
||||
Success=MoveFile(LongName1.c_str(),LongName2.c_str())!=0;
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
std::string SrcNameA,DestNameA;
|
||||
WideToChar(SrcName,SrcNameA);
|
||||
WideToChar(DestName,DestNameA);
|
||||
bool Success=rename(SrcNameA.c_str(),DestNameA.c_str())==0;
|
||||
return Success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool DelFile(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=DeleteFile(Name.c_str())!=0;
|
||||
if (!Success)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
Success=DeleteFile(LongName.c_str())!=0;
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
bool Success=remove(NameA.c_str())==0;
|
||||
return Success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool DelDir(const std::wstring &Name)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=RemoveDirectory(Name.c_str())!=0;
|
||||
if (!Success)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
Success=RemoveDirectory(LongName.c_str())!=0;
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
std::string NameA;
|
||||
WideToChar(Name,NameA);
|
||||
bool Success=rmdir(NameA.c_str())==0;
|
||||
return Success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
bool SetFileCompression(const std::wstring &Name,bool State)
|
||||
{
|
||||
HANDLE hFile=CreateFile(Name.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::wstring LongName;
|
||||
if (GetWinLongPath(Name,LongName))
|
||||
hFile=CreateFile(LongName.c_str(),FILE_READ_DATA|FILE_WRITE_DATA,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
||||
if (hFile==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
bool Success=SetFileCompression(hFile,State);
|
||||
CloseHandle(hFile);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool SetFileCompression(HANDLE hFile,bool State)
|
||||
{
|
||||
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
|
||||
DWORD Result;
|
||||
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
|
||||
sizeof(NewState),NULL,0,&Result,NULL);
|
||||
return RetCode!=0;
|
||||
}
|
||||
|
||||
|
||||
void ResetFileCache(const std::wstring &Name)
|
||||
{
|
||||
// To reset file cache in Windows it is enough to open it with
|
||||
// FILE_FLAG_NO_BUFFERING and then close it.
|
||||
HANDLE hSrc=CreateFile(Name.c_str(),GENERIC_READ,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL);
|
||||
if (hSrc!=INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hSrc);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Delete symbolic links in file path, if any, and replace them by directories.
|
||||
// Prevents extracting files outside of destination folder with symlink chains.
|
||||
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked)
|
||||
{
|
||||
// Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
|
||||
// "lnk1/../dir", but converts the path to "dir". In Unix we need to call
|
||||
// this function to prevent placing unpacked files outside of destination
|
||||
// folder if previously we unpacked "dir/lnk1" -> "..",
|
||||
// "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
|
||||
// We may still need this function to prevent abusing symlink chains
|
||||
// in link source path if we remove detection of such chains
|
||||
// in IsRelativeSymlinkSafe. This function seems to make other symlink
|
||||
// related safety checks redundant, but for now we prefer to keep them too.
|
||||
//
|
||||
// 2022.12.01: the performance impact is minimized after adding the check
|
||||
// against the previous path and enabling this verification only after
|
||||
// extracting a symlink with ".." in target. So we enabled it for Windows
|
||||
// as well for extra safety.
|
||||
//#ifdef _UNIX
|
||||
std::wstring Path=SrcName;
|
||||
|
||||
size_t SkipLength=SkipPart.size();
|
||||
|
||||
if (SkipLength>0 && !starts_with(Path,SkipPart))
|
||||
SkipLength=0; // Parameter validation, not really needed now.
|
||||
|
||||
// Do not check parts already checked in previous path to improve performance.
|
||||
for (size_t I=0;I<Path.size() && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
|
||||
if (IsPathDiv(Path[I]) && I>SkipLength)
|
||||
SkipLength=I;
|
||||
|
||||
// Avoid converting symlinks in destination path part specified by user.
|
||||
while (SkipLength<Path.size() && IsPathDiv(Path[SkipLength]))
|
||||
SkipLength++;
|
||||
|
||||
if (Path.size()>0)
|
||||
for (size_t I=Path.size()-1;I>SkipLength;I--)
|
||||
if (IsPathDiv(Path[I]))
|
||||
{
|
||||
Path.erase(I);
|
||||
FindData FD;
|
||||
if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// Normally Windows symlinks to directory look like a directory
|
||||
// and are deleted with DelDir(). It is possible to create
|
||||
// a file-like symlink pointing at directory, which can be deleted
|
||||
// only with && DelFile, but such symlink isn't really functional.
|
||||
// Here we prefer to fail deleting such symlink and skip extracting
|
||||
// a file.
|
||||
if (!DelDir(Path))
|
||||
#else
|
||||
if (!DelFile(Path))
|
||||
#endif
|
||||
{
|
||||
ErrHandler.CreateErrorMsg(SrcName); // Extraction command will skip this file or directory.
|
||||
return false; // Couldn't delete the symlink to replace it with directory.
|
||||
}
|
||||
}
|
||||
}
|
||||
LastChecked=SrcName;
|
||||
//#endif
|
||||
return true;
|
||||
}
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
#ifndef _RAR_FILEFN_
|
||||
#define _RAR_FILEFN_
|
||||
|
||||
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
|
||||
|
||||
MKDIR_CODE MakeDir(const std::wstring &Name,bool SetAttr,uint Attr);
|
||||
bool CreateDir(const std::wstring &Name);
|
||||
bool CreatePath(const std::wstring &Path,bool SkipLastName,bool Silent);
|
||||
|
||||
void SetDirTime(const std::wstring &Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
|
||||
|
||||
|
||||
bool IsRemovable(const std::wstring &Name);
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 GetFreeDisk(const std::wstring &Name);
|
||||
#endif
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
|
||||
bool IsFAT(const std::wstring &Root);
|
||||
#endif
|
||||
|
||||
bool FileExist(const std::wstring &Name);
|
||||
bool WildFileExist(const std::wstring &Name);
|
||||
bool IsDir(uint Attr);
|
||||
bool IsUnreadable(uint Attr);
|
||||
bool IsLink(uint Attr);
|
||||
void SetSFXMode(const std::wstring &FileName);
|
||||
void EraseDiskContents(const std::wstring &FileName);
|
||||
bool IsDeleteAllowed(uint FileAttr);
|
||||
void PrepareToDelete(const std::wstring &Name);
|
||||
uint GetFileAttr(const std::wstring &Name);
|
||||
bool SetFileAttr(const std::wstring &Name,uint Attr);
|
||||
bool MkTemp(std::wstring &Name,const wchar *Ext);
|
||||
|
||||
enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
|
||||
|
||||
void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
|
||||
|
||||
bool RenameFile(const std::wstring &SrcName,const std::wstring &DestName);
|
||||
bool DelFile(const std::wstring &Name);
|
||||
bool DelDir(const std::wstring &Name);
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE)
|
||||
bool SetFileCompression(const std::wstring &Name,bool State);
|
||||
bool SetFileCompression(HANDLE hFile,bool State);
|
||||
void ResetFileCache(const std::wstring &Name);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Keep it here and not in extinfo.cpp, because it is invoked from Zip.SFX too.
|
||||
bool LinksToDirs(const std::wstring &SrcName,const std::wstring &SkipPart,std::wstring &LastChecked);
|
||||
|
||||
#endif
|
||||
Vendored
+163
@@ -0,0 +1,163 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
bool ReadTextFile(
|
||||
const std::wstring &Name,
|
||||
StringList *List,
|
||||
bool Config,
|
||||
bool AbortOnError,
|
||||
RAR_CHARSET SrcCharset,
|
||||
bool Unquote,
|
||||
bool SkipComments,
|
||||
bool ExpandEnvStr)
|
||||
{
|
||||
std::wstring FileName;
|
||||
|
||||
if (Config)
|
||||
GetConfigName(Name,FileName,true,false);
|
||||
else
|
||||
FileName=Name;
|
||||
|
||||
File SrcFile;
|
||||
if (!FileName.empty())
|
||||
{
|
||||
bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
|
||||
|
||||
if (!OpenCode)
|
||||
{
|
||||
if (AbortOnError)
|
||||
ErrHandler.Exit(RARX_OPEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
SrcFile.SetHandleType(FILE_HANDLESTD);
|
||||
|
||||
size_t DataSize=0,ReadSize;
|
||||
const int ReadBlock=4096;
|
||||
|
||||
std::vector<byte> Data(ReadBlock);
|
||||
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
|
||||
{
|
||||
DataSize+=ReadSize;
|
||||
Data.resize(DataSize+ReadBlock); // Always have ReadBlock available for next data.
|
||||
}
|
||||
// Set to really read size, so we can zero terminate it correctly.
|
||||
Data.resize(DataSize);
|
||||
|
||||
int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
|
||||
int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
|
||||
bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
|
||||
|
||||
if (SrcCharset==RCH_DEFAULT)
|
||||
SrcCharset=DetectTextEncoding(Data.data(),DataSize);
|
||||
|
||||
std::vector<wchar> DataW(ReadBlock);
|
||||
|
||||
if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
|
||||
{
|
||||
Data.push_back(0); // Zero terminate.
|
||||
#if defined(_WIN_ALL)
|
||||
if (SrcCharset==RCH_OEM)
|
||||
OemToCharA((char *)Data.data(),(char *)Data.data());
|
||||
#endif
|
||||
DataW.resize(Data.size());
|
||||
CharToWide((char *)Data.data(),DataW.data(),DataW.size());
|
||||
}
|
||||
|
||||
if (SrcCharset==RCH_UNICODE)
|
||||
{
|
||||
size_t Start=2; // Skip byte order mark.
|
||||
if (!LittleEndian && !BigEndian) // No byte order mask.
|
||||
{
|
||||
Start=0;
|
||||
LittleEndian=1;
|
||||
}
|
||||
|
||||
DataW.resize(Data.size()/2+1);
|
||||
size_t End=Data.size() & ~1; // We need even bytes number for UTF-16.
|
||||
for (size_t I=Start;I<End;I+=2)
|
||||
DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256;
|
||||
DataW[(End-Start)/2]=0;
|
||||
}
|
||||
|
||||
if (SrcCharset==RCH_UTF8)
|
||||
{
|
||||
Data.push_back(0); // Zero terminate data.
|
||||
DataW.resize(Data.size());
|
||||
UtfToWide((const char *)(Data.data()+(Utf8 ? 3:0)),DataW.data(),DataW.size());
|
||||
}
|
||||
|
||||
wchar *CurStr=DataW.data();
|
||||
|
||||
while (*CurStr!=0)
|
||||
{
|
||||
wchar *NextStr=CurStr,*CmtPtr=NULL;
|
||||
while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
|
||||
{
|
||||
if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
|
||||
{
|
||||
*NextStr=0;
|
||||
CmtPtr=NextStr;
|
||||
}
|
||||
NextStr++;
|
||||
}
|
||||
bool Done=*NextStr==0;
|
||||
|
||||
*NextStr=0;
|
||||
for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
|
||||
{
|
||||
if (*SpacePtr!=' ' && *SpacePtr!='\t')
|
||||
break;
|
||||
*SpacePtr=0;
|
||||
}
|
||||
|
||||
if (Unquote && *CurStr=='\"')
|
||||
{
|
||||
size_t Length=wcslen(CurStr);
|
||||
if (CurStr[Length-1]=='\"')
|
||||
{
|
||||
CurStr[Length-1]=0;
|
||||
CurStr++;
|
||||
}
|
||||
}
|
||||
|
||||
bool Expanded=false;
|
||||
#if defined(_WIN_ALL)
|
||||
if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
|
||||
{
|
||||
std::wstring ExpName=CurStr;
|
||||
ExpandEnvironmentStr(ExpName);
|
||||
if (!ExpName.empty())
|
||||
List->AddString(ExpName);
|
||||
Expanded=true;
|
||||
}
|
||||
#endif
|
||||
if (!Expanded && *CurStr!=0)
|
||||
List->AddString(CurStr);
|
||||
|
||||
if (Done)
|
||||
break;
|
||||
CurStr=NextStr+1;
|
||||
while (*CurStr=='\r' || *CurStr=='\n')
|
||||
CurStr++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize)
|
||||
{
|
||||
if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf &&
|
||||
IsTextUtf8(Data+3,DataSize-3))
|
||||
return RCH_UTF8;
|
||||
|
||||
bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254;
|
||||
bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255;
|
||||
|
||||
if (LittleEndian || BigEndian)
|
||||
for (size_t I=LittleEndian ? 3 : 2;I<DataSize;I+=2)
|
||||
if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n')
|
||||
return RCH_UNICODE; // High byte in UTF-16 char is found.
|
||||
|
||||
return RCH_DEFAULT;
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
#ifndef _RAR_FILESTR_
|
||||
#define _RAR_FILESTR_
|
||||
|
||||
bool ReadTextFile(
|
||||
const std::wstring &Name,
|
||||
StringList *List,
|
||||
bool Config,
|
||||
bool AbortOnError=false,
|
||||
RAR_CHARSET SrcCharset=RCH_DEFAULT,
|
||||
bool Unquote=false,
|
||||
bool SkipComments=false,
|
||||
bool ExpandEnvStr=false
|
||||
);
|
||||
|
||||
RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize);
|
||||
|
||||
#endif
|
||||
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
FindFile::FindFile()
|
||||
{
|
||||
FirstCall=true;
|
||||
#ifdef _WIN_ALL
|
||||
hFind=INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dirp=NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
FindFile::~FindFile()
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
#else
|
||||
if (dirp!=NULL)
|
||||
closedir(dirp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void FindFile::SetMask(const std::wstring &Mask)
|
||||
{
|
||||
FindMask=Mask;
|
||||
FirstCall=true;
|
||||
}
|
||||
|
||||
|
||||
bool FindFile::Next(FindData *fd,bool GetSymLink)
|
||||
{
|
||||
fd->Error=false;
|
||||
if (FindMask.empty())
|
||||
return false;
|
||||
#ifdef _WIN_ALL
|
||||
if (FirstCall)
|
||||
{
|
||||
if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
#else
|
||||
if (FirstCall)
|
||||
{
|
||||
std::wstring DirName;
|
||||
DirName=FindMask;
|
||||
RemoveNameFromPath(DirName);
|
||||
if (DirName.empty())
|
||||
DirName=L".";
|
||||
std::string DirNameA;
|
||||
WideToChar(DirName,DirNameA);
|
||||
if ((dirp=opendir(DirNameA.c_str()))==NULL)
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
std::wstring Name;
|
||||
struct dirent *ent=readdir(dirp);
|
||||
if (ent==NULL)
|
||||
return false;
|
||||
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
|
||||
continue;
|
||||
if (!CharToWide(std::string(ent->d_name),Name))
|
||||
uiMsg(UIERROR_INVALIDNAME,L"",Name);
|
||||
|
||||
if (CmpName(FindMask,Name,MATCH_NAMES))
|
||||
{
|
||||
std::wstring FullName=FindMask;
|
||||
FullName.erase(GetNamePos(FullName));
|
||||
if (FullName.size()+Name.size()>=MAXPATHSIZE)
|
||||
{
|
||||
uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
|
||||
return false;
|
||||
}
|
||||
FullName+=Name;
|
||||
if (!FastFind(FullName,fd,GetSymLink))
|
||||
{
|
||||
ErrHandler.OpenErrorMsg(FullName);
|
||||
continue;
|
||||
}
|
||||
fd->Name=FullName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fd->Flags=0;
|
||||
fd->IsDir=IsDir(fd->FileAttr);
|
||||
fd->IsLink=IsLink(fd->FileAttr);
|
||||
|
||||
FirstCall=false;
|
||||
std::wstring NameOnly=PointToName(fd->Name);
|
||||
if (NameOnly==L"." || NameOnly==L"..")
|
||||
return Next(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FindFile::FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink)
|
||||
{
|
||||
fd->Error=false;
|
||||
#ifndef _UNIX
|
||||
if (IsWildcard(FindMask))
|
||||
return false;
|
||||
#endif
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
FindClose(hFind);
|
||||
#elif defined(_UNIX)
|
||||
std::string FindMaskA;
|
||||
WideToChar(FindMask,FindMaskA);
|
||||
|
||||
struct stat st;
|
||||
if (GetSymLink)
|
||||
{
|
||||
#ifdef SAVE_LINKS
|
||||
if (lstat(FindMaskA.c_str(),&st)!=0)
|
||||
#else
|
||||
if (stat(FindMaskA.c_str(),&st)!=0)
|
||||
#endif
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (stat(FindMaskA.c_str(),&st)!=0)
|
||||
{
|
||||
fd->Error=(errno!=ENOENT);
|
||||
return false;
|
||||
}
|
||||
fd->FileAttr=st.st_mode;
|
||||
fd->Size=st.st_size;
|
||||
|
||||
File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime);
|
||||
|
||||
fd->Name=FindMask;
|
||||
#endif
|
||||
fd->Flags=0;
|
||||
fd->IsDir=IsDir(fd->FileAttr);
|
||||
fd->IsLink=IsLink(fd->FileAttr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE FindFile::Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd)
|
||||
{
|
||||
WIN32_FIND_DATA FindData;
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
hFind=FindFirstFile(Mask.c_str(),&FindData);
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::wstring LongMask;
|
||||
if (GetWinLongPath(Mask,LongMask))
|
||||
hFind=FindFirstFile(LongMask.c_str(),&FindData);
|
||||
}
|
||||
if (hFind==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
int SysErr=GetLastError();
|
||||
// We must not issue an error for "file not found" and "path not found",
|
||||
// because it is normal to not find anything for wildcard mask when
|
||||
// archiving. Also searching for non-existent file is normal in some
|
||||
// other modules, like WinRAR scanning for winrar_theme_description.txt
|
||||
// to check if any themes are available.
|
||||
fd->Error=SysErr!=ERROR_FILE_NOT_FOUND &&
|
||||
SysErr!=ERROR_PATH_NOT_FOUND &&
|
||||
SysErr!=ERROR_NO_MORE_FILES;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!FindNextFile(hFind,&FindData))
|
||||
{
|
||||
hFind=INVALID_HANDLE_VALUE;
|
||||
fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
if (hFind!=INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fd->Name=Mask;
|
||||
SetName(fd->Name,FindData.cFileName);
|
||||
fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
|
||||
fd->FileAttr=FindData.dwFileAttributes;
|
||||
fd->ftCreationTime=FindData.ftCreationTime;
|
||||
fd->ftLastAccessTime=FindData.ftLastAccessTime;
|
||||
fd->ftLastWriteTime=FindData.ftLastWriteTime;
|
||||
fd->mtime.SetWinFT(&FindData.ftLastWriteTime);
|
||||
fd->ctime.SetWinFT(&FindData.ftCreationTime);
|
||||
fd->atime.SetWinFT(&FindData.ftLastAccessTime);
|
||||
|
||||
|
||||
}
|
||||
fd->Flags=0;
|
||||
return hFind;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
#ifndef _RAR_FINDDATA_
|
||||
#define _RAR_FINDDATA_
|
||||
|
||||
enum FINDDATA_FLAGS {
|
||||
FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
|
||||
};
|
||||
|
||||
struct FindData
|
||||
{
|
||||
std::wstring Name;
|
||||
uint64 Size;
|
||||
uint FileAttr;
|
||||
bool IsDir;
|
||||
bool IsLink;
|
||||
RarTime mtime;
|
||||
RarTime ctime;
|
||||
RarTime atime;
|
||||
#ifdef _WIN_ALL
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
#endif
|
||||
uint Flags;
|
||||
bool Error;
|
||||
};
|
||||
|
||||
class FindFile
|
||||
{
|
||||
private:
|
||||
#ifdef _WIN_ALL
|
||||
static HANDLE Win32Find(HANDLE hFind,const std::wstring &Mask,FindData *fd);
|
||||
#endif
|
||||
|
||||
std::wstring FindMask;
|
||||
bool FirstCall;
|
||||
#ifdef _WIN_ALL
|
||||
HANDLE hFind;
|
||||
#else
|
||||
DIR *dirp;
|
||||
#endif
|
||||
public:
|
||||
FindFile();
|
||||
~FindFile();
|
||||
void SetMask(const std::wstring &Mask);
|
||||
bool Next(FindData *fd,bool GetSymLink=false);
|
||||
static bool FastFind(const std::wstring &FindMask,FindData *fd,bool GetSymLink=false);
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
BitInput::BitInput(bool AllocBuffer)
|
||||
{
|
||||
ExternalBuffer=false;
|
||||
if (AllocBuffer)
|
||||
{
|
||||
// getbits*() attempt to read data from InAddr, ... InAddr+8 positions.
|
||||
// So let's allocate 8 additional bytes for situation, when we need to
|
||||
// read only 1 byte from the last position of buffer and avoid a crash
|
||||
// from access to next 8 bytes, which contents we do not need.
|
||||
size_t BufSize=MAX_SIZE+8;
|
||||
InBuf=new byte[BufSize];
|
||||
|
||||
// Ensure that we get predictable results when accessing bytes in area
|
||||
// not filled with read data.
|
||||
memset(InBuf,0,BufSize);
|
||||
}
|
||||
else
|
||||
InBuf=nullptr;
|
||||
}
|
||||
|
||||
|
||||
BitInput::~BitInput()
|
||||
{
|
||||
if (!ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
}
|
||||
|
||||
|
||||
void BitInput::faddbits(uint Bits)
|
||||
{
|
||||
// Function wrapped version of inline addbits to reduce the code size.
|
||||
addbits(Bits);
|
||||
}
|
||||
|
||||
|
||||
uint BitInput::fgetbits()
|
||||
{
|
||||
// Function wrapped version of inline getbits to reduce the code size.
|
||||
return getbits();
|
||||
}
|
||||
|
||||
|
||||
void BitInput::SetExternalBuffer(byte *Buf)
|
||||
{
|
||||
if (InBuf!=nullptr && !ExternalBuffer)
|
||||
delete[] InBuf;
|
||||
InBuf=Buf;
|
||||
ExternalBuffer=true;
|
||||
}
|
||||
|
||||
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
#ifndef _RAR_GETBITS_
|
||||
#define _RAR_GETBITS_
|
||||
|
||||
class BitInput
|
||||
{
|
||||
public:
|
||||
enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
|
||||
|
||||
int InAddr; // Curent byte position in the buffer.
|
||||
int InBit; // Current bit position in the current byte.
|
||||
|
||||
bool ExternalBuffer;
|
||||
public:
|
||||
BitInput(bool AllocBuffer);
|
||||
~BitInput();
|
||||
|
||||
byte *InBuf; // Dynamically allocated input buffer.
|
||||
|
||||
void InitBitInput()
|
||||
{
|
||||
InAddr=InBit=0;
|
||||
}
|
||||
|
||||
// Move forward by 'Bits' bits.
|
||||
void addbits(uint Bits)
|
||||
{
|
||||
Bits+=InBit;
|
||||
InAddr+=Bits>>3;
|
||||
InBit=Bits&7;
|
||||
}
|
||||
|
||||
// Return 16 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits()
|
||||
{
|
||||
#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
|
||||
uint32 BitField=RawGetBE4(InBuf+InAddr);
|
||||
BitField >>= (16-InBit);
|
||||
#else
|
||||
uint BitField=(uint)InBuf[InAddr] << 16;
|
||||
BitField|=(uint)InBuf[InAddr+1] << 8;
|
||||
BitField|=(uint)InBuf[InAddr+2];
|
||||
BitField >>= (8-InBit);
|
||||
#endif
|
||||
return BitField & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
// Return 32 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint getbits32()
|
||||
{
|
||||
uint BitField=RawGetBE4(InBuf+InAddr);
|
||||
BitField <<= InBit;
|
||||
BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
|
||||
return BitField & 0xffffffff;
|
||||
}
|
||||
|
||||
// Return 64 bits from current position in the buffer.
|
||||
// Bit at (InAddr,InBit) has the highest position in returning data.
|
||||
uint64 getbits64()
|
||||
{
|
||||
uint64 BitField=RawGetBE8(InBuf+InAddr);
|
||||
BitField <<= InBit;
|
||||
BitField|=(uint)InBuf[InAddr+8] >> (8-InBit);
|
||||
return BitField;
|
||||
}
|
||||
|
||||
void faddbits(uint Bits);
|
||||
uint fgetbits();
|
||||
|
||||
// Check if buffer has enough space for IncPtr bytes. Returns 'true'
|
||||
// if buffer will be overflown.
|
||||
bool Overflow(uint IncPtr)
|
||||
{
|
||||
return InAddr+IncPtr>=MAX_SIZE;
|
||||
}
|
||||
|
||||
void SetExternalBuffer(byte *Buf);
|
||||
};
|
||||
#endif
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
#define INCLUDEGLOBAL
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#include "rar.hpp"
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#ifndef _RAR_GLOBAL_
|
||||
#define _RAR_GLOBAL_
|
||||
|
||||
#ifdef INCLUDEGLOBAL
|
||||
#define EXTVAR
|
||||
#else
|
||||
#define EXTVAR extern
|
||||
#endif
|
||||
|
||||
EXTVAR ErrorHandler ErrHandler;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
bool ExtractHardlink(CommandData *Cmd,const std::wstring &NameNew,const std::wstring &NameExisting)
|
||||
{
|
||||
if (!FileExist(NameExisting))
|
||||
{
|
||||
uiMsg(UIERROR_HLINKCREATE,NameNew);
|
||||
uiMsg(UIERROR_NOLINKTARGET);
|
||||
ErrHandler.SetErrorCode(RARX_CREATE);
|
||||
return false;
|
||||
}
|
||||
CreatePath(NameNew,true,Cmd->DisableNames);
|
||||
|
||||
#ifdef _WIN_ALL
|
||||
bool Success=CreateHardLink(NameNew.c_str(),NameExisting.c_str(),NULL)!=0;
|
||||
if (!Success)
|
||||
{
|
||||
uiMsg(UIERROR_HLINKCREATE,NameNew);
|
||||
ErrHandler.SysErrMsg();
|
||||
ErrHandler.SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
return Success;
|
||||
#elif defined(_UNIX)
|
||||
std::string NameExistingA,NameNewA;
|
||||
WideToChar(NameExisting,NameExistingA);
|
||||
WideToChar(NameNew,NameNewA);
|
||||
bool Success=link(NameExistingA.c_str(),NameNewA.c_str())==0;
|
||||
if (!Success)
|
||||
{
|
||||
uiMsg(UIERROR_HLINKCREATE,NameNew);
|
||||
ErrHandler.SysErrMsg();
|
||||
ErrHandler.SetErrorCode(RARX_CREATE);
|
||||
}
|
||||
return Success;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Vendored
+283
@@ -0,0 +1,283 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
void HashValue::Init(HASH_TYPE Type)
|
||||
{
|
||||
HashValue::Type=Type;
|
||||
|
||||
// Zero length data CRC32 is 0. It is important to set it when creating
|
||||
// headers with no following data like directories or symlinks.
|
||||
if (Type==HASH_RAR14 || Type==HASH_CRC32)
|
||||
CRC32=0;
|
||||
if (Type==HASH_BLAKE2)
|
||||
{
|
||||
// dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
|
||||
// is BLAKE2sp hash of empty data. We init the structure to this value,
|
||||
// so if we create a file or service header with no following data like
|
||||
// "file copy" or "symlink", we set the checksum to proper value avoiding
|
||||
// additional header type or size checks when extracting.
|
||||
static byte EmptyHash[32]={
|
||||
0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
|
||||
0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
|
||||
0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
|
||||
0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
|
||||
};
|
||||
memcpy(Digest,EmptyHash,sizeof(Digest));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool HashValue::operator == (const HashValue &cmp) const
|
||||
{
|
||||
if (Type==HASH_NONE || cmp.Type==HASH_NONE)
|
||||
return true;
|
||||
if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
|
||||
Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
|
||||
return CRC32==cmp.CRC32;
|
||||
if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
|
||||
return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
DataHash::DataHash()
|
||||
{
|
||||
blake2ctx=NULL;
|
||||
HashType=HASH_NONE;
|
||||
#ifdef RAR_SMP
|
||||
ThPool=NULL;
|
||||
MaxThreads=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
DataHash::~DataHash()
|
||||
{
|
||||
#ifdef RAR_SMP
|
||||
delete ThPool;
|
||||
#endif
|
||||
cleandata(&CurCRC32, sizeof(CurCRC32));
|
||||
if (blake2ctx!=NULL)
|
||||
{
|
||||
cleandata(blake2ctx, sizeof(blake2sp_state));
|
||||
delete blake2ctx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
|
||||
{
|
||||
if (blake2ctx==NULL)
|
||||
blake2ctx=new blake2sp_state;
|
||||
HashType=Type;
|
||||
if (Type==HASH_RAR14)
|
||||
CurCRC32=0;
|
||||
if (Type==HASH_CRC32)
|
||||
CurCRC32=0xffffffff; // Initial CRC32 value.
|
||||
if (Type==HASH_BLAKE2)
|
||||
blake2sp_init(blake2ctx);
|
||||
#ifdef RAR_SMP
|
||||
DataHash::MaxThreads=Min(MaxThreads,HASH_POOL_THREADS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Update(const void *Data,size_t DataSize)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
if (HashType==HASH_RAR14)
|
||||
CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
|
||||
#endif
|
||||
if (HashType==HASH_CRC32)
|
||||
{
|
||||
#ifdef RAR_SMP
|
||||
UpdateCRC32MT(Data,DataSize);
|
||||
#else
|
||||
CurCRC32=CRC32(CurCRC32,Data,DataSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (HashType==HASH_BLAKE2)
|
||||
{
|
||||
#ifdef RAR_SMP
|
||||
if (MaxThreads>1 && ThPool==nullptr)
|
||||
ThPool=new ThreadPool(HASH_POOL_THREADS);
|
||||
blake2ctx->ThPool=ThPool;
|
||||
blake2ctx->MaxThreads=MaxThreads;
|
||||
#endif
|
||||
blake2sp_update( blake2ctx, (byte *)Data, DataSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
THREAD_PROC(BuildCRC32Thread)
|
||||
{
|
||||
DataHash::CRC32ThreadData *td=(DataHash::CRC32ThreadData *)Data;
|
||||
|
||||
// Use 0 initial value to simplify combining the result with existing CRC32.
|
||||
// It doesn't affect the first initial 0xffffffff in the data beginning.
|
||||
// If we used 0xffffffff here, we would need to shift 0xffffffff left to
|
||||
// block width and XOR it with block CRC32 to reset its initial value to 0.
|
||||
td->DataCRC=CRC32(0,td->Data,td->DataSize);
|
||||
}
|
||||
|
||||
|
||||
// CRC is linear and distributive over addition, so CRC(a+b)=CRC(a)+CRC(b).
|
||||
// Since addition in finite field is XOR, we have CRC(a^b)=CRC(a)^CRC(b).
|
||||
// So CRC(aaabbb) = CRC(aaa000) ^ CRC(000bbb) = CRC(aaa000) ^ CRC(bbb),
|
||||
// because CRC ignores leading zeroes. Thus to split CRC calculations
|
||||
// to "aaa" and "bbb" blocks and then to threads we need to be able to
|
||||
// find CRC(aaa000) knowing "aaa" quickly. We use Galois finite field to
|
||||
// calculate the power of 2 to get "1000" and multiply it by "aaa".
|
||||
void DataHash::UpdateCRC32MT(const void *Data,size_t DataSize)
|
||||
{
|
||||
const size_t MinBlock=0x4000;
|
||||
if (DataSize<2*MinBlock || MaxThreads<2)
|
||||
{
|
||||
CurCRC32=CRC32(CurCRC32,Data,DataSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ThPool==nullptr)
|
||||
ThPool=new ThreadPool(HASH_POOL_THREADS);
|
||||
|
||||
size_t Threads=MaxThreads;
|
||||
size_t BlockSize=DataSize/Threads;
|
||||
if (BlockSize<MinBlock)
|
||||
{
|
||||
BlockSize=MinBlock;
|
||||
Threads=DataSize/BlockSize;
|
||||
}
|
||||
|
||||
CRC32ThreadData td[MaxPoolThreads];
|
||||
|
||||
//#undef USE_THREADS
|
||||
for (size_t I=0;I<Threads;I++)
|
||||
{
|
||||
td[I].Data=(byte*)Data+I*BlockSize;
|
||||
td[I].DataSize=(I+1==Threads) ? DataSize-I*BlockSize : BlockSize;
|
||||
#ifdef USE_THREADS
|
||||
ThPool->AddTask(BuildCRC32Thread,(void*)&td[I]);
|
||||
#else
|
||||
BuildCRC32Thread((void*)&td[I]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_THREADS
|
||||
ThPool->WaitDone();
|
||||
#endif // USE_THREADS
|
||||
|
||||
uint StdShift=gfExpCRC(uint(8*td[0].DataSize));
|
||||
for (size_t I=0;I<Threads;I++)
|
||||
{
|
||||
// Prepare the multiplier to shift CRC to proper position.
|
||||
uint ShiftMult;
|
||||
if (td[I].DataSize==td[0].DataSize)
|
||||
ShiftMult=StdShift; // Reuse the shift value for typical block size.
|
||||
else
|
||||
ShiftMult=gfExpCRC(uint(8*td[I].DataSize)); // 2 power "shift bits".
|
||||
|
||||
// To combine the cumulative total and current block CRC32, we multiply
|
||||
// the total data CRC32 to shift value to place it to proper position.
|
||||
// Invoke BitReverse32(), because 0xEDB88320 is the reversed polynomial.
|
||||
// Alternatively we could adjust the multiplication function for reversed
|
||||
// polynomials, but it would make it less readable without real speed gain.
|
||||
// If CRC32 threads used 0xffffffff initial value, we would need
|
||||
// to XOR the total data CRC32 with 0xffffffff before multiplication,
|
||||
// so 0xffffffff is also shifted left to current block width and replaces
|
||||
// the initial 0xffffffff CRC32 value with 0 in the current block CRC32
|
||||
// after XOR'ing it with total data CRC32. Since now CRC32 threads use 0
|
||||
// initial value, this is not necessary.
|
||||
CurCRC32=BitReverse32(gfMulCRC(BitReverse32(CurCRC32), ShiftMult));
|
||||
|
||||
// Combine the total data and current block CRC32.
|
||||
CurCRC32^=td[I].DataCRC;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint DataHash::BitReverse32(uint N)
|
||||
{
|
||||
uint Reversed=0;
|
||||
for (uint I=0;I<32;I++,N>>=1)
|
||||
Reversed|=(N & 1)<<(31-I);
|
||||
return Reversed;
|
||||
}
|
||||
|
||||
|
||||
// Galois field multiplication modulo POLY.
|
||||
uint DataHash::gfMulCRC(uint A, uint B)
|
||||
{
|
||||
// For reversed 0xEDB88320 polynomial we bit reverse CRC32 before passing
|
||||
// to this function, so we must use the normal polynomial here.
|
||||
// We set the highest polynomial bit 33 for proper multiplication
|
||||
// in case uint is larger than 32-bit.
|
||||
const uint POLY=uint(0x104c11db7);
|
||||
|
||||
uint R = 0 ; // Multiplication result.
|
||||
while (A != 0 && B != 0) // If any of multipliers becomes 0, quit early.
|
||||
{
|
||||
// For non-zero lowest B bit, add A to result.
|
||||
R ^= (B & 1)!=0 ? A : 0;
|
||||
|
||||
// Make A twice larger before the next iteration.
|
||||
// Subtract POLY to keep it modulo POLY if high bit is set.
|
||||
A = (A << 1) ^ ((A & 0x80000000)!=0 ? POLY : 0);
|
||||
|
||||
B >>= 1; // Move next B bit to lowest position.
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
|
||||
// Calculate 2 power N with square-and-multiply algorithm.
|
||||
uint DataHash::gfExpCRC(uint N)
|
||||
{
|
||||
uint S = 2; // Starts from base value and contains the current square.
|
||||
uint R = 1; // Exponentiation result.
|
||||
while (N > 1)
|
||||
{
|
||||
if ((N & 1)!=0) // If N is odd.
|
||||
R = gfMulCRC(R, S);
|
||||
S = gfMulCRC(S, S); // Next square.
|
||||
N >>= 1;
|
||||
}
|
||||
// We could change the loop condition to N > 0 and return R at expense
|
||||
// of one additional gfMulCRC(S, S).
|
||||
return gfMulCRC(R, S);
|
||||
}
|
||||
|
||||
|
||||
void DataHash::Result(HashValue *Result)
|
||||
{
|
||||
Result->Type=HashType;
|
||||
if (HashType==HASH_RAR14)
|
||||
Result->CRC32=CurCRC32;
|
||||
if (HashType==HASH_CRC32)
|
||||
Result->CRC32=CurCRC32^0xffffffff;
|
||||
if (HashType==HASH_BLAKE2)
|
||||
{
|
||||
// Preserve the original context, so we can continue hashing if necessary.
|
||||
blake2sp_state res=*blake2ctx;
|
||||
blake2sp_final(&res,Result->Digest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint DataHash::GetCRC32()
|
||||
{
|
||||
return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
|
||||
}
|
||||
|
||||
|
||||
bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
|
||||
{
|
||||
HashValue Final;
|
||||
Result(&Final);
|
||||
#ifndef RAR_NOCRYPT
|
||||
if (Key!=nullptr)
|
||||
ConvertHashToMAC(&Final,Key);
|
||||
#endif
|
||||
return Final==*CmpValue;
|
||||
}
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
#ifndef _RAR_DATAHASH_
|
||||
#define _RAR_DATAHASH_
|
||||
|
||||
enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
|
||||
|
||||
struct HashValue
|
||||
{
|
||||
void Init(HASH_TYPE Type);
|
||||
|
||||
// Use the const member, so types on both sides of "==" match.
|
||||
// Otherwise clang -std=c++20 issues "ambiguity is between a regular call
|
||||
// to this operator and a call with the argument order reversed" warning.
|
||||
bool operator == (const HashValue &cmp) const;
|
||||
|
||||
// Not actually used now. Const member for same reason as operator == above.
|
||||
// Can be removed after switching to C++20, which automatically provides "!="
|
||||
// if operator == is defined.
|
||||
bool operator != (const HashValue &cmp) const {return !(*this==cmp);}
|
||||
|
||||
HASH_TYPE Type;
|
||||
union
|
||||
{
|
||||
uint CRC32;
|
||||
byte Digest[SHA256_DIGEST_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#ifdef RAR_SMP
|
||||
class ThreadPool;
|
||||
class DataHash;
|
||||
#endif
|
||||
|
||||
|
||||
class DataHash
|
||||
{
|
||||
public:
|
||||
struct CRC32ThreadData
|
||||
{
|
||||
void *Data;
|
||||
size_t DataSize;
|
||||
uint DataCRC;
|
||||
};
|
||||
private:
|
||||
void UpdateCRC32MT(const void *Data,size_t DataSize);
|
||||
uint BitReverse32(uint N);
|
||||
uint gfMulCRC(uint A, uint B);
|
||||
uint gfExpCRC(uint N);
|
||||
|
||||
// Speed gain seems to vanish above 8 CRC32 threads.
|
||||
static const uint CRC32_POOL_THREADS=8;
|
||||
// Thread pool must allow at least BLAKE2_THREADS_NUMBER threads.
|
||||
static const uint HASH_POOL_THREADS=Max(BLAKE2_THREADS_NUMBER,CRC32_POOL_THREADS);
|
||||
|
||||
HASH_TYPE HashType;
|
||||
uint CurCRC32;
|
||||
blake2sp_state *blake2ctx;
|
||||
|
||||
#ifdef RAR_SMP
|
||||
ThreadPool *ThPool;
|
||||
|
||||
uint MaxThreads;
|
||||
#endif
|
||||
public:
|
||||
DataHash();
|
||||
~DataHash();
|
||||
void Init(HASH_TYPE Type,uint MaxThreads);
|
||||
void Update(const void *Data,size_t DataSize);
|
||||
void Result(HashValue *Result);
|
||||
uint GetCRC32();
|
||||
bool Cmp(HashValue *CmpValue,byte *Key);
|
||||
HASH_TYPE Type() {return HashType;}
|
||||
};
|
||||
|
||||
#endif
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
void FileHeader::Reset(size_t SubDataSize)
|
||||
{
|
||||
SubData.resize(SubDataSize);
|
||||
BaseBlock::Reset();
|
||||
FileHash.Init(HASH_NONE);
|
||||
mtime.Reset();
|
||||
atime.Reset();
|
||||
ctime.Reset();
|
||||
SplitBefore=false;
|
||||
SplitAfter=false;
|
||||
|
||||
UnknownUnpSize=0;
|
||||
|
||||
SubFlags=0; // Important for RAR 3.0 subhead.
|
||||
|
||||
CryptMethod=CRYPT_NONE;
|
||||
Encrypted=false;
|
||||
SaltSet=false;
|
||||
UsePswCheck=false;
|
||||
UseHashKey=false;
|
||||
Lg2Count=0;
|
||||
|
||||
Solid=false;
|
||||
Dir=false;
|
||||
WinSize=0;
|
||||
Inherited=false;
|
||||
SubBlock=false;
|
||||
CommentInHeader=false;
|
||||
Version=false;
|
||||
LargeFile=false;
|
||||
|
||||
RedirType=FSREDIR_NONE;
|
||||
DirTarget=false;
|
||||
UnixOwnerSet=false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
FileHeader& FileHeader::operator = (FileHeader &hd)
|
||||
{
|
||||
SubData.Reset();
|
||||
memcpy(this,&hd,sizeof(*this));
|
||||
SubData.CleanData();
|
||||
SubData=hd.SubData;
|
||||
return *this;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void MainHeader::Reset()
|
||||
{
|
||||
*this={};
|
||||
}
|
||||
Vendored
+357
@@ -0,0 +1,357 @@
|
||||
#ifndef _RAR_HEADERS_
|
||||
#define _RAR_HEADERS_
|
||||
|
||||
#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header.
|
||||
#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header.
|
||||
#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
|
||||
#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
|
||||
#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
|
||||
#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size.
|
||||
#define SIZEOF_LONGBLOCKHEAD 11
|
||||
#define SIZEOF_SUBBLOCKHEAD 14
|
||||
#define SIZEOF_COMMHEAD 13
|
||||
#define SIZEOF_PROTECTHEAD 26
|
||||
#define SIZEOF_STREAMHEAD 26
|
||||
|
||||
#define VER_PACK 29U
|
||||
#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive.
|
||||
#define VER_PACK7 70U // It is stored as 1, but we subtract 70 when saving an archive.
|
||||
#define VER_UNPACK 29U
|
||||
#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive.
|
||||
#define VER_UNPACK7 70U // It is stored as 1, but we add 50 when reading an archive.
|
||||
#define VER_UNKNOWN 9999U // Just some large value.
|
||||
|
||||
#define MHD_VOLUME 0x0001U
|
||||
|
||||
// Old style main archive comment embed into main archive header. Must not
|
||||
// be used in new archives anymore.
|
||||
#define MHD_COMMENT 0x0002U
|
||||
|
||||
#define MHD_LOCK 0x0004U
|
||||
#define MHD_SOLID 0x0008U
|
||||
#define MHD_PACK_COMMENT 0x0010U
|
||||
#define MHD_NEWNUMBERING 0x0010U
|
||||
#define MHD_AV 0x0020U
|
||||
#define MHD_PROTECT 0x0040U
|
||||
#define MHD_PASSWORD 0x0080U
|
||||
#define MHD_FIRSTVOLUME 0x0100U
|
||||
|
||||
#define LHD_SPLIT_BEFORE 0x0001U
|
||||
#define LHD_SPLIT_AFTER 0x0002U
|
||||
#define LHD_PASSWORD 0x0004U
|
||||
|
||||
// Old style file comment embed into file header. Must not be used
|
||||
// in new archives anymore.
|
||||
#define LHD_COMMENT 0x0008U
|
||||
|
||||
// For non-file subheaders it denotes 'subblock having a parent file' flag.
|
||||
#define LHD_SOLID 0x0010U
|
||||
|
||||
|
||||
#define LHD_WINDOWMASK 0x00e0U
|
||||
#define LHD_WINDOW64 0x0000U
|
||||
#define LHD_WINDOW128 0x0020U
|
||||
#define LHD_WINDOW256 0x0040U
|
||||
#define LHD_WINDOW512 0x0060U
|
||||
#define LHD_WINDOW1024 0x0080U
|
||||
#define LHD_WINDOW2048 0x00a0U
|
||||
#define LHD_WINDOW4096 0x00c0U
|
||||
#define LHD_DIRECTORY 0x00e0U
|
||||
|
||||
#define LHD_LARGE 0x0100U
|
||||
#define LHD_UNICODE 0x0200U
|
||||
#define LHD_SALT 0x0400U
|
||||
#define LHD_VERSION 0x0800U
|
||||
#define LHD_EXTTIME 0x1000U
|
||||
|
||||
#define SKIP_IF_UNKNOWN 0x4000U
|
||||
#define LONG_BLOCK 0x8000U
|
||||
|
||||
#define EARC_NEXT_VOLUME 0x0001U // Not last volume.
|
||||
#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
|
||||
#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record.
|
||||
#define EARC_VOLNUMBER 0x0008U // Store a number of current volume.
|
||||
|
||||
enum HEADER_TYPE {
|
||||
// RAR 5.0 header types.
|
||||
HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
|
||||
HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,
|
||||
|
||||
// RAR 1.5 - 4.x header types.
|
||||
HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
|
||||
HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
|
||||
HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
|
||||
};
|
||||
|
||||
|
||||
// RAR 2.9 and earlier service haeders, mostly outdated and not supported.
|
||||
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
|
||||
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
|
||||
|
||||
|
||||
// Internal implementation, depends on archive format version.
|
||||
enum HOST_SYSTEM {
|
||||
// RAR 5.0 host OS
|
||||
HOST5_WINDOWS=0,HOST5_UNIX=1,
|
||||
|
||||
// RAR 3.0 host OS.
|
||||
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
|
||||
HOST_BEOS=5,HOST_MAX
|
||||
};
|
||||
|
||||
// Unified archive format independent implementation.
|
||||
enum HOST_SYSTEM_TYPE {
|
||||
HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
|
||||
};
|
||||
|
||||
|
||||
// We also use these values in extra field, so do not modify them.
|
||||
enum FILE_SYSTEM_REDIRECT {
|
||||
FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
|
||||
FSREDIR_HARDLINK, FSREDIR_FILECOPY
|
||||
};
|
||||
|
||||
|
||||
#define SUBHEAD_TYPE_CMT L"CMT"
|
||||
#define SUBHEAD_TYPE_QOPEN L"QO"
|
||||
#define SUBHEAD_TYPE_ACL L"ACL"
|
||||
#define SUBHEAD_TYPE_STREAM L"STM"
|
||||
#define SUBHEAD_TYPE_UOWNER L"UOW"
|
||||
#define SUBHEAD_TYPE_AV L"AV"
|
||||
#define SUBHEAD_TYPE_RR L"RR"
|
||||
#define SUBHEAD_TYPE_OS2EA L"EA2"
|
||||
|
||||
/* new file inherits a subblock when updating a host file */
|
||||
#define SUBHEAD_FLAGS_INHERITED 0x80000000
|
||||
|
||||
#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
|
||||
|
||||
|
||||
struct MarkHeader
|
||||
{
|
||||
byte Mark[8];
|
||||
|
||||
// Following fields are virtual and not present in real blocks.
|
||||
uint HeadSize;
|
||||
};
|
||||
|
||||
|
||||
struct BaseBlock
|
||||
{
|
||||
uint HeadCRC; // 'ushort' for RAR 1.5.
|
||||
HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
|
||||
uint Flags; // 'ushort' for RAR 1.5.
|
||||
uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.
|
||||
|
||||
bool SkipIfUnknown;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
SkipIfUnknown=false;
|
||||
}
|
||||
|
||||
// We use it to assign this block data to inherited blocks.
|
||||
// Such function seems to be cleaner than '(BaseBlock&)' cast or adding
|
||||
// 'using BaseBlock::operator=;' to every inherited header.
|
||||
void SetBaseBlock(BaseBlock &Src)
|
||||
{
|
||||
*this=Src;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BlockHeader:BaseBlock
|
||||
{
|
||||
uint DataSize;
|
||||
};
|
||||
|
||||
|
||||
struct MainHeader:BaseBlock
|
||||
{
|
||||
ushort HighPosAV;
|
||||
uint PosAV;
|
||||
bool CommentInHeader;
|
||||
bool PackComment; // For RAR 1.4 archive format only.
|
||||
bool Locator;
|
||||
uint64 QOpenOffset; // Offset of quick list record.
|
||||
uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
|
||||
uint64 RROffset; // Offset of recovery record.
|
||||
uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
|
||||
size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
|
||||
std::wstring OrigName; // Original archive name.
|
||||
RarTime OrigTime; // Original archive time.
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
|
||||
struct FileHeader:BlockHeader
|
||||
{
|
||||
byte HostOS;
|
||||
uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
|
||||
byte Method;
|
||||
union {
|
||||
uint FileAttr;
|
||||
uint SubFlags;
|
||||
};
|
||||
std::wstring FileName;
|
||||
|
||||
std::vector<byte> SubData;
|
||||
|
||||
RarTime mtime;
|
||||
RarTime ctime;
|
||||
RarTime atime;
|
||||
|
||||
int64 PackSize;
|
||||
int64 UnpSize;
|
||||
int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.
|
||||
|
||||
HashValue FileHash;
|
||||
|
||||
uint FileFlags;
|
||||
|
||||
bool SplitBefore;
|
||||
bool SplitAfter;
|
||||
|
||||
bool UnknownUnpSize;
|
||||
|
||||
bool Encrypted;
|
||||
CRYPT_METHOD CryptMethod;
|
||||
bool SaltSet;
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte InitV[SIZE_INITV];
|
||||
bool UsePswCheck;
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
|
||||
// Use HMAC calculated from HashKey and checksum instead of plain checksum.
|
||||
bool UseHashKey;
|
||||
|
||||
// Key to convert checksum to HMAC. Derived from password with PBKDF2
|
||||
// using additional iterations.
|
||||
byte HashKey[SHA256_DIGEST_SIZE];
|
||||
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
|
||||
bool Solid;
|
||||
bool Dir;
|
||||
bool CommentInHeader; // RAR 2.0 file comment.
|
||||
bool Version; // name.ext;ver file name containing the version number.
|
||||
uint64 WinSize;
|
||||
bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
|
||||
|
||||
// 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
|
||||
bool LargeFile;
|
||||
|
||||
// 'true' for HEAD_SERVICE block, which is a child of preceding file block.
|
||||
// RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
|
||||
bool SubBlock;
|
||||
|
||||
HOST_SYSTEM_TYPE HSType;
|
||||
|
||||
FILE_SYSTEM_REDIRECT RedirType;
|
||||
std::wstring RedirName;
|
||||
bool DirTarget;
|
||||
|
||||
bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
|
||||
char UnixOwnerName[256],UnixGroupName[256];
|
||||
#ifdef _UNIX
|
||||
uid_t UnixOwnerID;
|
||||
gid_t UnixGroupID;
|
||||
#else // Need these Unix fields in Windows too for 'list' command.
|
||||
uint UnixOwnerID;
|
||||
uint UnixGroupID;
|
||||
#endif
|
||||
|
||||
void Reset(size_t SubDataSize=0);
|
||||
|
||||
bool CmpName(const wchar *Name)
|
||||
{
|
||||
return FileName==Name;
|
||||
}
|
||||
|
||||
// FileHeader& operator = (FileHeader &hd);
|
||||
};
|
||||
|
||||
|
||||
struct EndArcHeader:BaseBlock
|
||||
{
|
||||
// Optional CRC32 of entire archive up to start of EndArcHeader block.
|
||||
// Present in RAR 4.x archives if EARC_DATACRC flag is set.
|
||||
uint ArcDataCRC;
|
||||
|
||||
uint VolNumber; // Optional number of current volume.
|
||||
|
||||
// 7 additional zero bytes can be stored here if EARC_REVSPACE is set.
|
||||
|
||||
bool NextVolume; // Not last volume.
|
||||
bool DataCRC;
|
||||
bool RevSpace;
|
||||
bool StoreVolNumber;
|
||||
void Reset()
|
||||
{
|
||||
BaseBlock::Reset();
|
||||
NextVolume=false;
|
||||
DataCRC=false;
|
||||
RevSpace=false;
|
||||
StoreVolNumber=false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CryptHeader:BaseBlock
|
||||
{
|
||||
bool UsePswCheck;
|
||||
uint Lg2Count; // Log2 of PBKDF2 repetition count.
|
||||
byte Salt[SIZE_SALT50];
|
||||
byte PswCheck[SIZE_PSWCHECK];
|
||||
};
|
||||
|
||||
|
||||
// SubBlockHeader and its successors were used in RAR 2.x format.
|
||||
// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
|
||||
struct SubBlockHeader:BlockHeader
|
||||
{
|
||||
ushort SubType;
|
||||
byte Level;
|
||||
};
|
||||
|
||||
|
||||
struct CommentHeader:BaseBlock
|
||||
{
|
||||
ushort UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
ushort CommCRC;
|
||||
};
|
||||
|
||||
|
||||
struct ProtectHeader:BlockHeader
|
||||
{
|
||||
byte Version;
|
||||
ushort RecSectors;
|
||||
uint TotalBlocks;
|
||||
byte Mark[8];
|
||||
};
|
||||
|
||||
|
||||
struct EAHeader:SubBlockHeader
|
||||
{
|
||||
uint UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
uint EACRC;
|
||||
};
|
||||
|
||||
|
||||
struct StreamHeader:SubBlockHeader
|
||||
{
|
||||
uint UnpSize;
|
||||
byte UnpVer;
|
||||
byte Method;
|
||||
uint StreamCRC;
|
||||
ushort StreamNameSize;
|
||||
std::string StreamName;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
#ifndef _RAR_HEADERS5_
|
||||
#define _RAR_HEADERS5_
|
||||
|
||||
#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
|
||||
#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
|
||||
|
||||
// RAR 5.0 block flags common for all blocks.
|
||||
|
||||
// Additional extra area is present in the end of block header.
|
||||
#define HFL_EXTRA 0x0001
|
||||
// Additional data area is present in the end of block header.
|
||||
#define HFL_DATA 0x0002
|
||||
// Unknown blocks with this flag must be skipped when updating an archive.
|
||||
#define HFL_SKIPIFUNKNOWN 0x0004
|
||||
// Data area of this block is continuing from previous volume.
|
||||
#define HFL_SPLITBEFORE 0x0008
|
||||
// Data area of this block is continuing in next volume.
|
||||
#define HFL_SPLITAFTER 0x0010
|
||||
// Block depends on preceding file block.
|
||||
#define HFL_CHILD 0x0020
|
||||
// Preserve a child block if host is modified.
|
||||
#define HFL_INHERITED 0x0040
|
||||
|
||||
// RAR 5.0 main archive header specific flags.
|
||||
#define MHFL_VOLUME 0x0001 // Volume.
|
||||
#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
|
||||
#define MHFL_SOLID 0x0004 // Solid archive.
|
||||
#define MHFL_PROTECT 0x0008 // Recovery record is present.
|
||||
#define MHFL_LOCK 0x0010 // Locked archive.
|
||||
|
||||
// RAR 5.0 file header specific flags.
|
||||
#define FHFL_DIRECTORY 0x0001 // Directory.
|
||||
#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
|
||||
#define FHFL_CRC32 0x0004 // CRC32 field is present.
|
||||
#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
|
||||
|
||||
// RAR 5.0 end of archive header specific flags.
|
||||
#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
|
||||
|
||||
// RAR 5.0 archive encryption header specific flags.
|
||||
#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
|
||||
|
||||
|
||||
// RAR 5.0 file compression flags.
|
||||
#define FCI_ALGO_BIT0 0x00000001 // Version of compression algorithm.
|
||||
#define FCI_ALGO_BIT1 0x00000002 // 0 .. 63.
|
||||
#define FCI_ALGO_BIT2 0x00000004
|
||||
#define FCI_ALGO_BIT3 0x00000008
|
||||
#define FCI_ALGO_BIT4 0x00000010
|
||||
#define FCI_ALGO_BIT5 0x00000020
|
||||
#define FCI_SOLID 0x00000040 // Solid flag.
|
||||
#define FCI_METHOD_BIT0 0x00000080 // Compression method.
|
||||
#define FCI_METHOD_BIT1 0x00000100 // 0 .. 5 (6 and 7 are not used).
|
||||
#define FCI_METHOD_BIT2 0x00000200
|
||||
#define FCI_DICT_BIT0 0x00000400 // Dictionary size.
|
||||
#define FCI_DICT_BIT1 0x00000800 // 128 KB .. 1 TB.
|
||||
#define FCI_DICT_BIT2 0x00001000
|
||||
#define FCI_DICT_BIT3 0x00002000
|
||||
#define FCI_DICT_BIT4 0x00004000
|
||||
#define FCI_DICT_FRACT0 0x00008000 // Dictionary fraction in 1/32 of size.
|
||||
#define FCI_DICT_FRACT1 0x00010000
|
||||
#define FCI_DICT_FRACT2 0x00020000
|
||||
#define FCI_DICT_FRACT3 0x00040000
|
||||
#define FCI_DICT_FRACT4 0x00080000
|
||||
#define FCI_RAR5_COMPAT 0x00100000 // RAR7 compression flags and RAR5 compression algorithm.
|
||||
|
||||
// Main header extra field values.
|
||||
#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
|
||||
#define MHEXTRA_METADATA 0x02 // Archive metadata.
|
||||
|
||||
// Flags for MHEXTRA_LOCATOR.
|
||||
#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
|
||||
#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
|
||||
|
||||
// Flags for MHEXTRA_METADATA.
|
||||
#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present.
|
||||
#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present.
|
||||
#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format.
|
||||
#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision.
|
||||
|
||||
// File and service header extra field values.
|
||||
#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
|
||||
#define FHEXTRA_HASH 0x02 // File hash.
|
||||
#define FHEXTRA_HTIME 0x03 // High precision file time.
|
||||
#define FHEXTRA_VERSION 0x04 // File version information.
|
||||
#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
|
||||
#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
|
||||
#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
|
||||
|
||||
|
||||
// Hash type values for FHEXTRA_HASH.
|
||||
#define FHEXTRA_HASH_BLAKE2 0x00
|
||||
|
||||
// Flags for FHEXTRA_HTIME.
|
||||
#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
|
||||
#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
|
||||
#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
|
||||
#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
|
||||
#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision.
|
||||
|
||||
// Flags for FHEXTRA_CRYPT.
|
||||
#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
|
||||
#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
|
||||
|
||||
// Flags for FHEXTRA_REDIR.
|
||||
#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
|
||||
|
||||
// Flags for FHEXTRA_UOWNER.
|
||||
#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
|
||||
#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
|
||||
#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
|
||||
#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
|
||||
|
||||
#endif
|
||||
Vendored
+111
@@ -0,0 +1,111 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
DWORD WinNT()
|
||||
{
|
||||
static int dwPlatformId=-1;
|
||||
static DWORD dwMajorVersion,dwMinorVersion;
|
||||
if (dwPlatformId==-1)
|
||||
{
|
||||
OSVERSIONINFO WinVer;
|
||||
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
|
||||
GetVersionEx(&WinVer);
|
||||
dwPlatformId=WinVer.dwPlatformId;
|
||||
dwMajorVersion=WinVer.dwMajorVersion;
|
||||
dwMinorVersion=WinVer.dwMinorVersion;
|
||||
|
||||
}
|
||||
DWORD Result=0;
|
||||
if (dwPlatformId==VER_PLATFORM_WIN32_NT)
|
||||
Result=dwMajorVersion*0x100+dwMinorVersion;
|
||||
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Since Windows 10 development is stopped, we can assume that its build
|
||||
// number never reaches 22000. So we do not need WMI anymore.
|
||||
#if 0
|
||||
// Replace it with documented Windows 11 check when available.
|
||||
#include <comdef.h>
|
||||
#include <Wbemidl.h>
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
||||
static bool WMI_IsWindows10()
|
||||
{
|
||||
IWbemLocator *pLoc = NULL;
|
||||
|
||||
HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
|
||||
IID_IWbemLocator,(LPVOID *)&pLoc);
|
||||
|
||||
if (FAILED(hres))
|
||||
return false;
|
||||
|
||||
IWbemServices *pSvc = NULL;
|
||||
|
||||
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,0,NULL,NULL,&pSvc);
|
||||
|
||||
if (FAILED(hres))
|
||||
{
|
||||
pLoc->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,
|
||||
RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE);
|
||||
|
||||
if (FAILED(hres))
|
||||
{
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumWbemClassObject *pEnumerator = NULL;
|
||||
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"),
|
||||
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
|
||||
|
||||
if (FAILED(hres) || pEnumerator==NULL)
|
||||
{
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Win10=false;
|
||||
|
||||
IWbemClassObject *pclsObj = NULL;
|
||||
ULONG uReturn = 0;
|
||||
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
|
||||
if (pclsObj!=NULL && uReturn>0)
|
||||
{
|
||||
VARIANT vtProp;
|
||||
pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
|
||||
Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL;
|
||||
VariantClear(&vtProp);
|
||||
pclsObj->Release();
|
||||
}
|
||||
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
pEnumerator->Release();
|
||||
|
||||
return Win10;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Replace it with actual check when available.
|
||||
bool IsWindows11OrGreater()
|
||||
{
|
||||
static bool IsSet=false,IsWin11=false;
|
||||
if (!IsSet)
|
||||
{
|
||||
OSVERSIONINFO WinVer;
|
||||
WinVer.dwOSVersionInfoSize=sizeof(WinVer);
|
||||
GetVersionEx(&WinVer);
|
||||
IsWin11=WinVer.dwMajorVersion>10 ||
|
||||
WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000/* && !WMI_IsWindows10()*/;
|
||||
IsSet=true;
|
||||
}
|
||||
return IsWin11;
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
#ifndef _RAR_ISNT_
|
||||
#define _RAR_ISNT_
|
||||
|
||||
enum WINNT_VERSION {
|
||||
WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
|
||||
WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
|
||||
WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
|
||||
};
|
||||
|
||||
DWORD WinNT();
|
||||
|
||||
|
||||
// Replace it with actual check when available.
|
||||
bool IsWindows11OrGreater();
|
||||
|
||||
#endif
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
/*
|
||||
To enable, disable or check Large Memory pages manually:
|
||||
- open "Local Security Policy" from "Start Menu";
|
||||
- open "Lock Pages in Memory" in "Local Policies\User Rights Assignment";
|
||||
- add or remove the user and sign out and sign in or restart Windows.
|
||||
*/
|
||||
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
#define ALLOW_LARGE_PAGES
|
||||
#endif
|
||||
|
||||
LargePageAlloc::LargePageAlloc()
|
||||
{
|
||||
UseLargePages=false;
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
PageSize=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void LargePageAlloc::AllowLargePages(bool Allow)
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
if (Allow && PageSize==0)
|
||||
{
|
||||
HMODULE hKernel=GetModuleHandle(L"kernel32.dll");
|
||||
if (hKernel!=nullptr)
|
||||
{
|
||||
typedef SIZE_T (*GETLARGEPAGEMINIMUM)();
|
||||
GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum");
|
||||
if (pGetLargePageMinimum!=nullptr)
|
||||
PageSize=pGetLargePageMinimum();
|
||||
}
|
||||
if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME))
|
||||
{
|
||||
UseLargePages=false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UseLargePages=Allow;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool LargePageAlloc::IsPrivilegeAssigned()
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
return SetPrivilege(SE_LOCK_MEMORY_NAME);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool LargePageAlloc::AssignPrivilege()
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
HANDLE hToken = NULL;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// Get the required buffer size.
|
||||
DWORD BufSize=0;
|
||||
GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize);
|
||||
if (BufSize==0 || BufSize>1000000) // Sanity check for returned value.
|
||||
{
|
||||
CloseHandle(hToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize);
|
||||
|
||||
// Get the current user token information.
|
||||
if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0)
|
||||
{
|
||||
CloseHandle(hToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get SID string for the current user.
|
||||
LPWSTR ApiSidStr;
|
||||
ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr);
|
||||
|
||||
// Convert SID to C++ string and release API based buffer.
|
||||
std::wstring SidStr=ApiSidStr;
|
||||
LocalFree(ApiSidStr);
|
||||
CloseHandle(hToken);
|
||||
|
||||
if (IsUserAdmin())
|
||||
AssignPrivilegeBySid(SidStr);
|
||||
else
|
||||
{
|
||||
// Define here, so they survive until ShellExecuteEx call.
|
||||
std::wstring ExeName=GetModuleFileStr();
|
||||
std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr;
|
||||
|
||||
SHELLEXECUTEINFO shExecInfo{};
|
||||
shExecInfo.cbSize = sizeof(shExecInfo);
|
||||
|
||||
shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode.
|
||||
shExecInfo.lpVerb = L"runas";
|
||||
shExecInfo.lpFile = ExeName.c_str();
|
||||
shExecInfo.lpParameters = Param.c_str();
|
||||
shExecInfo.nShow = SW_SHOWNORMAL;
|
||||
BOOL Result=ShellExecuteEx(&shExecInfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid)
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
LSA_HANDLE PolicyHandle;
|
||||
LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it.
|
||||
|
||||
#ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR.
|
||||
// We define STATUS_SUCCESS here instead of including ntstatus.h to avoid
|
||||
// macro redefinition warnings. We tried UMDF_USING_NTSTATUS define
|
||||
// and other workarounds, but it didn't help.
|
||||
const uint STATUS_SUCCESS=0;
|
||||
#endif
|
||||
|
||||
if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT|
|
||||
POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS)
|
||||
return false;
|
||||
|
||||
PSID UserSid;
|
||||
ConvertStringSidToSid(Sid.c_str(),&UserSid);
|
||||
|
||||
LSA_UNICODE_STRING LsaString;
|
||||
LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME;
|
||||
// It must be in bytes, so multiple it to sizeof(wchar_t).
|
||||
LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]);
|
||||
LsaString.MaximumLength=LsaString.Length;
|
||||
|
||||
bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS;
|
||||
|
||||
LocalFree(UserSid);
|
||||
LsaClose(PolicyHandle);
|
||||
|
||||
mprintf(St(MPrivilegeAssigned));
|
||||
if (Ask(St(MYesNo)) == 1)
|
||||
Shutdown(POWERMODE_RESTART);
|
||||
|
||||
return Success;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool LargePageAlloc::AssignConfirmation()
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
mprintf(St(MLockInMemoryNeeded));
|
||||
return Ask(St(MYesNo)) == 1;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* LargePageAlloc::new_large(size_t Size)
|
||||
{
|
||||
void *Allocated=nullptr;
|
||||
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
if (UseLargePages && Size>=PageSize)
|
||||
{
|
||||
// VirtualAlloc fails if allocation size isn't multiple of page size.
|
||||
SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize;
|
||||
Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE);
|
||||
if (Allocated!=nullptr)
|
||||
LargeAlloc.push_back(Allocated);
|
||||
}
|
||||
#endif
|
||||
return Allocated;
|
||||
}
|
||||
|
||||
|
||||
bool LargePageAlloc::delete_large(void *Addr)
|
||||
{
|
||||
#ifdef ALLOW_LARGE_PAGES
|
||||
if (Addr!=nullptr)
|
||||
for (size_t I=0;I<LargeAlloc.size();I++)
|
||||
if (LargeAlloc[I]==Addr)
|
||||
{
|
||||
LargeAlloc[I]=nullptr;
|
||||
VirtualFree(Addr,0,MEM_RELEASE);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
#ifndef _RAR_LARGEPAGE_
|
||||
#define _RAR_LARGEPAGE_
|
||||
|
||||
class LargePageAlloc
|
||||
{
|
||||
private:
|
||||
static constexpr const wchar *LOCKMEM_SWITCH=L"isetup_privilege_lockmem";
|
||||
|
||||
void* new_large(size_t Size);
|
||||
bool delete_large(void *Addr);
|
||||
#ifdef _WIN_ALL
|
||||
std::vector<void*> LargeAlloc;
|
||||
SIZE_T PageSize;
|
||||
#endif
|
||||
bool UseLargePages;
|
||||
public:
|
||||
LargePageAlloc();
|
||||
void AllowLargePages(bool Allow);
|
||||
static bool IsPrivilegeAssigned();
|
||||
static bool AssignPrivilege();
|
||||
static bool AssignPrivilegeBySid(const std::wstring &Sid);
|
||||
static bool AssignConfirmation();
|
||||
|
||||
static bool ProcessSwitch(CommandData *Cmd,const wchar *Switch)
|
||||
{
|
||||
if (Switch[0]==LOCKMEM_SWITCH[0])
|
||||
{
|
||||
size_t Length=wcslen(LOCKMEM_SWITCH);
|
||||
if (wcsncmp(Switch,LOCKMEM_SWITCH,Length)==0)
|
||||
{
|
||||
LargePageAlloc::AssignPrivilegeBySid(Switch+Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T> T* new_l(size_t Size,bool Clear=false)
|
||||
{
|
||||
T *Allocated=(T*)new_large(Size*sizeof(T));
|
||||
if (Allocated==nullptr)
|
||||
Allocated=Clear ? new T[Size]{} : new T[Size];
|
||||
return Allocated;
|
||||
}
|
||||
|
||||
template <class T> void delete_l(T *Addr)
|
||||
{
|
||||
if (!delete_large(Addr))
|
||||
delete[] Addr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
****** ***** ****** UnRAR - free utility for RAR archives
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
****** ******* ****** License for use and distribution of
|
||||
** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
** ** ** ** ** ** FREE portable version
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The source code of UnRAR utility is freeware. This means:
|
||||
|
||||
1. All copyrights to RAR and the utility UnRAR are exclusively
|
||||
owned by the author - Alexander Roshal.
|
||||
|
||||
2. UnRAR source code may be used in any software to handle
|
||||
RAR archives without limitations free of charge, but cannot be
|
||||
used to develop RAR (WinRAR) compatible archiver and to
|
||||
re-create RAR compression algorithm, which is proprietary.
|
||||
Distribution of modified UnRAR source code in separate form
|
||||
or as a part of other software is permitted, provided that
|
||||
full text of this paragraph, starting from "UnRAR source code"
|
||||
words, is included in license, or in documentation if license
|
||||
is not available, and in source code comments of resulting package.
|
||||
|
||||
3. The UnRAR utility may be freely distributed. It is allowed
|
||||
to distribute UnRAR inside of other software packages.
|
||||
|
||||
4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
|
||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
||||
OR MISUSING THIS SOFTWARE.
|
||||
|
||||
5. Installing and using the UnRAR utility signifies acceptance of
|
||||
these terms and conditions of the license.
|
||||
|
||||
6. If you don't agree with terms of the license you must remove
|
||||
UnRAR files from your storage devices and cease to use the
|
||||
utility.
|
||||
|
||||
Thank you for your interest in RAR and UnRAR.
|
||||
|
||||
|
||||
Alexander L. Roshal
|
||||
Vendored
+501
@@ -0,0 +1,501 @@
|
||||
#include "rar.hpp"
|
||||
|
||||
static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames);
|
||||
static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
|
||||
static void ListOldSubHeader(Archive &Arc);
|
||||
static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
|
||||
|
||||
void ListArchive(CommandData *Cmd)
|
||||
{
|
||||
int64 SumPackSize=0,SumUnpSize=0;
|
||||
uint ArcCount=0,SumFileCount=0;
|
||||
bool Technical=(Cmd->Command[1]=='T');
|
||||
bool ShowService=Technical && Cmd->Command[2]=='A';
|
||||
bool Bare=(Cmd->Command[1]=='B');
|
||||
bool Verbose=(Cmd->Command[0]=='V');
|
||||
|
||||
std::wstring ArcName;
|
||||
while (Cmd->GetArcName(ArcName))
|
||||
{
|
||||
if (Cmd->ManualPassword)
|
||||
Cmd->Password.Clean(); // Clean user entered password before processing next archive.
|
||||
|
||||
Archive Arc(Cmd);
|
||||
if (!Arc.WOpen(ArcName))
|
||||
continue;
|
||||
bool FileMatched=true;
|
||||
while (true)
|
||||
{
|
||||
int64 TotalPackSize=0,TotalUnpSize=0;
|
||||
uint FileCount=0;
|
||||
if (Arc.IsArchive(true))
|
||||
{
|
||||
bool TitleShown=false;
|
||||
if (!Bare)
|
||||
{
|
||||
Arc.ViewComment();
|
||||
mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName.c_str());
|
||||
|
||||
mprintf(L"\n%s: ",St(MListDetails));
|
||||
const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 1.5":L"RAR 5");
|
||||
mprintf(L"%s", Fmt);
|
||||
if (Arc.Solid)
|
||||
mprintf(L", %s", St(MListSolid));
|
||||
if (Arc.SFXSize>0)
|
||||
mprintf(L", %s", St(MListSFX));
|
||||
if (Arc.Volume)
|
||||
if (Arc.Format==RARFMT50)
|
||||
{
|
||||
// RAR 5.0 archives store the volume number in main header,
|
||||
// so it is already available now.
|
||||
mprintf(L", ");
|
||||
mprintf(St(MVolumeNumber),Arc.VolNumber+1);
|
||||
}
|
||||
else
|
||||
mprintf(L", %s", St(MListVolume));
|
||||
if (Arc.Protected)
|
||||
mprintf(L", %s", St(MListRR));
|
||||
if (Arc.Locked)
|
||||
mprintf(L", %s", St(MListLock));
|
||||
if (Arc.Encrypted)
|
||||
mprintf(L", %s", St(MListEncHead));
|
||||
|
||||
if (!Arc.MainHead.OrigName.empty())
|
||||
mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str());
|
||||
if (Arc.MainHead.OrigTime.IsSet())
|
||||
{
|
||||
wchar DateStr[50];
|
||||
Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical);
|
||||
mprintf(L"\n%s: %s",St(MOriginalTime),DateStr);
|
||||
}
|
||||
|
||||
mprintf(L"\n");
|
||||
}
|
||||
|
||||
wchar VolNumText[50];
|
||||
*VolNumText=0;
|
||||
while (Arc.ReadHeader()>0)
|
||||
{
|
||||
Wait(); // Allow quit listing with Ctrl+C.
|
||||
HEADER_TYPE HeaderType=Arc.GetHeaderType();
|
||||
if (HeaderType==HEAD_ENDARC)
|
||||
{
|
||||
#ifndef SFX_MODULE
|
||||
// Only RAR 1.5 archives store the volume number in end record.
|
||||
if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15)
|
||||
swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1);
|
||||
#endif
|
||||
if (Technical && ShowService)
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListService),L"EOF");
|
||||
if (*VolNumText!=0)
|
||||
mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText);
|
||||
mprintf(L"\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch(HeaderType)
|
||||
{
|
||||
case HEAD_FILE:
|
||||
FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
|
||||
if (FileMatched)
|
||||
{
|
||||
ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames);
|
||||
if (!Arc.FileHead.SplitBefore)
|
||||
{
|
||||
TotalUnpSize+=Arc.FileHead.UnpSize;
|
||||
FileCount++;
|
||||
}
|
||||
TotalPackSize+=Arc.FileHead.PackSize;
|
||||
}
|
||||
break;
|
||||
case HEAD_SERVICE:
|
||||
// For service blocks dependent on previous block, such as ACL
|
||||
// or NTFS stream, we use "file matched" flag of host file.
|
||||
// Independent blocks like RR are matched separately,
|
||||
// so we can list them by their name. Also we match even
|
||||
// dependent blocks separately if "vta -idn" are set. User may
|
||||
// want to see service blocks only in this case.
|
||||
if (!Arc.SubHead.SubBlock || Cmd->DisableNames)
|
||||
FileMatched=Cmd->IsProcessFile(Arc.SubHead,NULL,MATCH_WILDSUBPATH,0,NULL)!=0;
|
||||
if (FileMatched && !Bare)
|
||||
{
|
||||
// Here we set DisableNames parameter to true regardless of
|
||||
// Cmd->DisableNames. If "vta -idn" are set together, user
|
||||
// wants to see service blocks like RR only.
|
||||
if (Technical && ShowService)
|
||||
ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Arc.SeekToNext();
|
||||
}
|
||||
if (!Bare && !Technical)
|
||||
if (TitleShown)
|
||||
{
|
||||
wchar UnpSizeText[20];
|
||||
itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
|
||||
wchar PackSizeText[20];
|
||||
itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
if (Verbose)
|
||||
{
|
||||
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
|
||||
mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
|
||||
PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
|
||||
VolNumText,FileCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
mprintf(L"\n----------- --------- ---------- ----- ----");
|
||||
mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
|
||||
}
|
||||
|
||||
SumFileCount+=FileCount;
|
||||
SumUnpSize+=TotalUnpSize;
|
||||
SumPackSize+=TotalPackSize;
|
||||
mprintf(L"\n");
|
||||
}
|
||||
else
|
||||
mprintf(St(MListNoFiles));
|
||||
|
||||
ArcCount++;
|
||||
|
||||
#ifndef NOVOLUME
|
||||
if (Cmd->VolSize==VOLSIZE_AUTO && (Arc.FileHead.SplitAfter ||
|
||||
Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
|
||||
MergeArchive(Arc,NULL,false,Cmd->Command[0]))
|
||||
Arc.Seek(0,SEEK_SET);
|
||||
else
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
|
||||
mprintf(St(MNotRAR),Arc.FileName.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean user entered password. Not really required, just for extra safety.
|
||||
if (Cmd->ManualPassword)
|
||||
Cmd->Password.Clean();
|
||||
|
||||
if (ArcCount>1 && !Bare && !Technical)
|
||||
{
|
||||
wchar UnpSizeText[20],PackSizeText[20];
|
||||
itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
if (Verbose)
|
||||
mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText,
|
||||
ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount);
|
||||
else
|
||||
mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum LISTCOL_TYPE {
|
||||
LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR
|
||||
};
|
||||
|
||||
|
||||
void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames)
|
||||
{
|
||||
if (!TitleShown && !Technical && !Bare)
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
mprintf(L"\n%ls",St(MListTitleV));
|
||||
if (!DisableNames)
|
||||
mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
|
||||
}
|
||||
else
|
||||
{
|
||||
mprintf(L"\n%ls",St(MListTitleL));
|
||||
if (!DisableNames)
|
||||
mprintf(L"\n----------- --------- ---------- ----- ----");
|
||||
}
|
||||
// Must be set even in DisableNames mode to suppress "0 files" output
|
||||
// unless no files are matched.
|
||||
TitleShown=true;
|
||||
}
|
||||
if (DisableNames)
|
||||
return;
|
||||
|
||||
const wchar *Name=hd.FileName.c_str();
|
||||
RARFORMAT Format=Arc.Format;
|
||||
|
||||
if (Bare)
|
||||
{
|
||||
mprintf(L"%s\n",Name);
|
||||
return;
|
||||
}
|
||||
|
||||
wchar UnpSizeText[30],PackSizeText[30];
|
||||
if (hd.UnpSize==INT64NDF)
|
||||
wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText));
|
||||
else
|
||||
itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
|
||||
itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
|
||||
|
||||
wchar AttrStr[30];
|
||||
if (hd.HeaderType==HEAD_SERVICE)
|
||||
swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.');
|
||||
else
|
||||
ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr));
|
||||
|
||||
wchar RatioStr[10];
|
||||
|
||||
if (hd.SplitBefore && hd.SplitAfter)
|
||||
wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr));
|
||||
else
|
||||
if (hd.SplitBefore)
|
||||
wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr));
|
||||
else
|
||||
if (hd.SplitAfter)
|
||||
wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr));
|
||||
else
|
||||
swprintf(RatioStr,ASIZE(RatioStr),L"%u%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
|
||||
|
||||
wchar DateStr[50];
|
||||
hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
|
||||
|
||||
if (Technical)
|
||||
{
|
||||
mprintf(L"\n%12s: %s",St(MListName),Name);
|
||||
|
||||
bool FileBlock=hd.HeaderType==HEAD_FILE;
|
||||
|
||||
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
|
||||
std::wstring StreamName=GetStreamNameNTFS(Arc);
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService);
|
||||
|
||||
if (hd.RedirType!=FSREDIR_NONE)
|
||||
switch(hd.RedirType)
|
||||
{
|
||||
case FSREDIR_UNIXSYMLINK:
|
||||
Type=St(MListUSymlink); break;
|
||||
case FSREDIR_WINSYMLINK:
|
||||
Type=St(MListWSymlink); break;
|
||||
case FSREDIR_JUNCTION:
|
||||
Type=St(MListJunction); break;
|
||||
case FSREDIR_HARDLINK:
|
||||
Type=St(MListHardlink); break;
|
||||
case FSREDIR_FILECOPY:
|
||||
Type=St(MListCopy); break;
|
||||
}
|
||||
mprintf(L"\n%12ls: %ls",St(MListType),Type);
|
||||
if (hd.RedirType!=FSREDIR_NONE)
|
||||
if (Format==RARFMT15)
|
||||
{
|
||||
std::string LinkTargetA;
|
||||
if (Arc.FileHead.Encrypted)
|
||||
{
|
||||
// Link data are encrypted. We would need to ask for password
|
||||
// and initialize decryption routine to display the link target.
|
||||
LinkTargetA="*<-?->";
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t DataSize=(size_t)Min(hd.PackSize,MAXPATHSIZE);
|
||||
std::vector<char> Buf(DataSize+1);
|
||||
Arc.Read(Buf.data(),DataSize);
|
||||
Buf[DataSize] = 0;
|
||||
LinkTargetA=Buf.data();
|
||||
}
|
||||
std::wstring LinkTarget;
|
||||
CharToWide(LinkTargetA,LinkTarget);
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget.c_str());
|
||||
}
|
||||
else
|
||||
mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName.c_str());
|
||||
}
|
||||
if (!hd.Dir)
|
||||
{
|
||||
mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
|
||||
mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
|
||||
mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
|
||||
|
||||
if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_RR))
|
||||
{
|
||||
// Display the original -rrN percent if available.
|
||||
int RecoveryPercent=Arc.GetRecoveryPercent();
|
||||
if (RecoveryPercent>0) // It can be -1 if failed to detect.
|
||||
mprintf(L"\n%12ls: %u%%",L"RR%", RecoveryPercent);
|
||||
}
|
||||
}
|
||||
bool WinTitles=false;
|
||||
#ifdef _WIN_ALL
|
||||
WinTitles=true;
|
||||
#endif
|
||||
if (hd.mtime.IsSet())
|
||||
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr);
|
||||
if (hd.ctime.IsSet())
|
||||
{
|
||||
hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
|
||||
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr);
|
||||
}
|
||||
if (hd.atime.IsSet())
|
||||
{
|
||||
hd.atime.GetText(DateStr,ASIZE(DateStr),true);
|
||||
mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr);
|
||||
}
|
||||
mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
|
||||
if (hd.FileHash.Type==HASH_CRC32)
|
||||
mprintf(L"\n%12ls: %8.8X",
|
||||
hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32",
|
||||
hd.FileHash.CRC32);
|
||||
if (hd.FileHash.Type==HASH_BLAKE2)
|
||||
{
|
||||
std::wstring BlakeStr;
|
||||
BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,BlakeStr);
|
||||
mprintf(L"\n%12ls: %ls",
|
||||
hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
|
||||
BlakeStr.c_str());
|
||||
}
|
||||
|
||||
const wchar *HostOS=L"";
|
||||
if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN)
|
||||
HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix";
|
||||
if (Format==RARFMT15)
|
||||
{
|
||||
static const wchar *RarOS[]={
|
||||
L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L""
|
||||
};
|
||||
if (hd.HostOS<ASIZE(RarOS))
|
||||
HostOS=RarOS[hd.HostOS];
|
||||
}
|
||||
if (*HostOS!=0)
|
||||
mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
|
||||
|
||||
std::wstring WinSize;
|
||||
if (!hd.Dir)
|
||||
if (hd.WinSize%1073741824==0)
|
||||
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1073741824) + L"g";
|
||||
else
|
||||
if (hd.WinSize%1048576==0)
|
||||
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1048576) + L"m";
|
||||
else
|
||||
if (hd.WinSize>=1024)
|
||||
WinSize=L" -md=" + std::to_wstring(hd.WinSize/1024) + L"k";
|
||||
else
|
||||
WinSize=L" -md=?";
|
||||
|
||||
mprintf(L"\n%12ls: RAR %ls(v%d) -m%d%s",St(MListCompInfo),
|
||||
Format==RARFMT15 ? L"1.5":L"5.0",
|
||||
hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,WinSize.c_str());
|
||||
|
||||
if (hd.Solid || hd.Encrypted)
|
||||
{
|
||||
mprintf(L"\n%12ls: ",St(MListFlags));
|
||||
if (hd.Solid)
|
||||
mprintf(L"%ls ",St(MListSolid));
|
||||
if (hd.Encrypted)
|
||||
mprintf(L"%ls ",St(MListEnc));
|
||||
}
|
||||
|
||||
if (hd.Version)
|
||||
{
|
||||
uint Version=ParseVersionFileName(hd.FileName,false);
|
||||
if (Version!=0)
|
||||
mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
|
||||
}
|
||||
|
||||
if (hd.UnixOwnerSet)
|
||||
{
|
||||
mprintf(L"\n%12ls: ",L"Unix owner");
|
||||
if (*hd.UnixOwnerName!=0)
|
||||
mprintf(L"%ls",GetWide(hd.UnixOwnerName).c_str());
|
||||
else
|
||||
if (hd.UnixOwnerNumeric)
|
||||
mprintf(L"#%d",hd.UnixOwnerID);
|
||||
mprintf(L":");
|
||||
if (*hd.UnixGroupName!=0)
|
||||
mprintf(L"%ls",GetWide(hd.UnixGroupName).c_str());
|
||||
else
|
||||
if (hd.UnixGroupNumeric)
|
||||
mprintf(L"#%d",hd.UnixGroupID);
|
||||
}
|
||||
|
||||
mprintf(L"\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
|
||||
|
||||
if (Verbose)
|
||||
mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
|
||||
|
||||
mprintf(L" %ls ",DateStr);
|
||||
|
||||
if (Verbose)
|
||||
{
|
||||
if (hd.FileHash.Type==HASH_CRC32)
|
||||
mprintf(L"%8.8X ",hd.FileHash.CRC32);
|
||||
else
|
||||
if (hd.FileHash.Type==HASH_BLAKE2)
|
||||
{
|
||||
byte *S=hd.FileHash.Digest;
|
||||
mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
|
||||
}
|
||||
else
|
||||
mprintf(hd.Dir ? L" ":L"???????? "); // Missing checksum is ok for folder, not for file.
|
||||
}
|
||||
mprintf(L"%ls",Name);
|
||||
}
|
||||
|
||||
|
||||
void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
|
||||
{
|
||||
switch(HostType)
|
||||
{
|
||||
case HSYS_WINDOWS:
|
||||
swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c",
|
||||
(A & 0x2000)!=0 ? 'I' : '.', // Not content indexed.
|
||||
(A & 0x0800)!=0 ? 'C' : '.', // Compressed.
|
||||
(A & 0x0020)!=0 ? 'A' : '.', // Archive.
|
||||
(A & 0x0010)!=0 ? 'D' : '.', // Directory.
|
||||
(A & 0x0004)!=0 ? 'S' : '.', // System.
|
||||
(A & 0x0002)!=0 ? 'H' : '.', // Hidden.
|
||||
(A & 0x0001)!=0 ? 'R' : '.'); // Read-only.
|
||||
break;
|
||||
case HSYS_UNIX:
|
||||
switch (A & 0xF000)
|
||||
{
|
||||
case 0x4000:
|
||||
AttrStr[0]='d';
|
||||
break;
|
||||
case 0xA000:
|
||||
AttrStr[0]='l';
|
||||
break;
|
||||
default:
|
||||
AttrStr[0]='-';
|
||||
break;
|
||||
}
|
||||
swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c",
|
||||
(A & 0x0100) ? 'r' : '-',
|
||||
(A & 0x0080) ? 'w' : '-',
|
||||
(A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'),
|
||||
(A & 0x0020) ? 'r' : '-',
|
||||
(A & 0x0010) ? 'w' : '-',
|
||||
(A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'),
|
||||
(A & 0x0004) ? 'r' : '-',
|
||||
(A & 0x0002) ? 'w' : '-',
|
||||
(A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
|
||||
break;
|
||||
case HSYS_UNKNOWN:
|
||||
wcsncpyz(AttrStr,L"?",AttrSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
#ifndef _RAR_LIST_
|
||||
#define _RAR_LIST_
|
||||
|
||||
void ListArchive(CommandData *Cmd);
|
||||
|
||||
#endif
|
||||
Loaded 100 of 192 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user