3032 lines
99 KiB
C
3032 lines
99 KiB
C
// par1.c
|
||
// Copyright : 2024-11-30 Yutaka Sawada
|
||
// License : GPL
|
||
|
||
#ifndef _UNICODE
|
||
#define _UNICODE
|
||
#endif
|
||
#ifndef UNICODE
|
||
#define UNICODE
|
||
#endif
|
||
#ifndef _WIN32_WINNT
|
||
#define _WIN32_WINNT 0x0601 // Windows 7 or later
|
||
#endif
|
||
|
||
#include <stdio.h>
|
||
|
||
#include <windows.h>
|
||
#include <imagehlp.h>
|
||
|
||
#include "common1.h"
|
||
#include "phmd5.h"
|
||
#include "md5_1.h"
|
||
#include "ini.h"
|
||
#include "par1.h"
|
||
#include "version.h"
|
||
|
||
/*
|
||
Fast Galois Field Arithmetic Library in C/C++
|
||
Copright (C) 2007 James S. Plank
|
||
*/
|
||
#include "gf8.h"
|
||
|
||
/*
|
||
// 行列の表示用関数
|
||
void galois_print_matrix(int *matrix, int rows, int cols)
|
||
{
|
||
int i, j;
|
||
printf("\n");
|
||
for (i = 0; i < rows; i++) {
|
||
for (j = 0; j < cols; j++) {
|
||
printf("%4d", matrix[i*cols+j]);
|
||
}
|
||
printf("\n");
|
||
}
|
||
}
|
||
*/
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
/*
|
||
gflib の行列作成用関数や行列の逆変換用の関数を元にして、
|
||
計算のやり方を PAR 1.0 用に修正する。
|
||
|
||
par-v1.1.tar.gz に含まれる rs.doc
|
||
Dummies guide to Reed-Solomon coding. を参考にする
|
||
|
||
解説ではパリティ計算用の行列の要素、掛ける値は
|
||
F(i,j) = i^(j-1) ・・・ i の (j-1) 乗
|
||
ソース・ブロックは i = 1~N で、
|
||
パリティ・ブロックは j = 1~M になってる。
|
||
|
||
実際に計算する関数では
|
||
ソース・ブロックが i = 0 ~ cols - 1 で、
|
||
パリティ・ブロックが j = 0 ~ rows - 1 なので、
|
||
|
||
理論のF(i,j) = F(i+1, j+1) = (i+1) ^ (j+1-1) = (i+1) ^ j
|
||
・・・ (i+1) の j 乗
|
||
*/
|
||
|
||
// PAR 1.0 のパリティ作成用の行列
|
||
int * make_encode_matrix(
|
||
int rows, // 横行、パリティのブロック数
|
||
int cols, // 縦列、ソースのブロック数
|
||
int first) // 最初のパリティ・ブロック番号
|
||
{
|
||
int *mat, i, j;
|
||
|
||
mat = malloc(sizeof(int) * rows * cols);
|
||
if (mat == NULL){
|
||
printf("malloc, %d\n", sizeof(int) * rows * cols);
|
||
return NULL;
|
||
}
|
||
|
||
for (j = 0; j < rows; j++){
|
||
for (i = 0; i < cols; i++)
|
||
mat[cols * j + i] = galois_power(i + 1, first + j);
|
||
}
|
||
|
||
return mat;
|
||
}
|
||
|
||
/*
|
||
失われたソース・ブロックの数 = 利用するパリティ・ブロックの数を縦行とし、
|
||
ソース・ブロックの数を横列とする長方形の行列を作る。
|
||
|
||
例えば、ソース・ブロックを A, B, C, D, E 、
|
||
そのパリティ・ブロックを X, Y, Z とする。
|
||
その場合の関係式は次のようになる。
|
||
|
||
1: 1*A xor 1*B xor 1*C xor 1*D xor 1*E = X
|
||
2: 1*A xor 2*B xor 3*C xor 4*D xor 5*E = Y
|
||
3: 1*A xor 4*B xor 5*C xor 16*D xor 17*E = Z
|
||
|
||
これは PARファイルを作成する際の行列と同じ。
|
||
1 1 1 1 1
|
||
1 2 3 4 5
|
||
1 4 5 16 17
|
||
|
||
ここで、B, C, D の三ブロックが失われて、それぞれを X, Y, Z で補うなら、
|
||
その状態は次のような関係式になる。
|
||
|
||
1: 1*A xor 1*B xor 1*C xor 1*D xor 1*E = 0*A xor 1*X xor 0*Y xor 0*Z xor 0*E
|
||
2: 1*A xor 2*B xor 3*C xor 4*D xor 5*E = 0*A xor 0*X xor 1*Y xor 0*Z xor 0*E
|
||
3: 1*A xor 4*B xor 5*C xor 16*D xor 17*E = 0*A xor 0*X xor 0*Y xor 1*Z xor 0*E
|
||
|
||
左:パリティ・ブロックの係数
|
||
1 1 1 1 1
|
||
1 2 3 4 5
|
||
1 4 5 16 17
|
||
|
||
右:そのパリティ・ブロックがどのソース・ブロックの位置にくるか
|
||
0 1 0 0 0
|
||
0 0 1 0 0
|
||
0 0 0 1 0
|
||
|
||
ここでまず、存在するソース・ブロックの列を 0にする。
|
||
|
||
1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E
|
||
2: 0*A xor 2*B xor 3*C xor 4*D xor 0*E = 1*A xor 0*X xor 1*Y xor 0*Z xor 5*E
|
||
3: 0*A xor 4*B xor 5*C xor 16*D xor 0*E = 1*A xor 0*X xor 0*Y xor 1*Z xor 17*E
|
||
|
||
左:ソース・ブロックが存在した所が 0になる
|
||
0 1 1 1 0
|
||
0 2 3 4 0
|
||
0 4 5 16 0
|
||
|
||
右:逆にこちらではソース・ブロックが存在した所が反転する
|
||
1 1 0 0 1
|
||
1 0 1 0 5
|
||
1 0 0 1 17
|
||
|
||
Gaussian Elimination (ガウスの消去法) で元の左の行列を右のに変換する
|
||
|
||
各行で 0でない値を探して、それが 1になるように、何倍かする。
|
||
まずは 1行目:
|
||
|
||
1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E
|
||
1 行目の 2列目は 1なのでそのまま。
|
||
|
||
他の行の同じ列に 0でない値があれば、それを 0にするために、
|
||
行を何倍かしたものを XOR する
|
||
|
||
2: 0*A xor 2*B xor 3*C xor 4*D xor 0*E = 1*A xor 0*X xor 1*Y xor 0*Z xor 5*E
|
||
2 行目の 2列目は 2なので、1行目の各値を 2倍したものを XOR する。
|
||
|
||
3: 0*A xor 4*B xor 5*C xor 16*D xor 0*E = 1*A xor 0*X xor 0*Y xor 1*Z xor 17*E
|
||
3 行目の 2列目は 4なので、1行目の各値を 4倍したものを XOR する。
|
||
|
||
1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E
|
||
2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E
|
||
3: 0*A xor 0*B xor 1*C xor 20*D xor 0*E = 5*A xor 4*X xor 0*Y xor 1*Z xor 21*E
|
||
|
||
次は 2行目:
|
||
|
||
2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E
|
||
2 行目の 3列目は 1なのでそのまま。
|
||
|
||
1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E
|
||
1 行目の 3列目は 1なので、1行目の各値をそのまま XOR する。
|
||
|
||
3: 0*A xor 0*B xor 1*C xor 20*D xor 0*E = 5*A xor 4*X xor 0*Y xor 1*Z xor 21*E
|
||
3 行目の 3列目は 1なので、1行目の各値をそのまま XOR する。
|
||
|
||
1: 0*A xor 1*B xor 0*C xor 7*D xor 0*E = 2*A xor 3*X xor 1*Y xor 0*Z xor 6*E
|
||
2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E
|
||
3: 0*A xor 0*B xor 0*C xor 18*D xor 0*E = 6*A xor 6*X xor 1*Y xor 1*Z xor 18*E
|
||
|
||
次は 3行目:
|
||
|
||
3: 0*A xor 0*B xor 0*C xor 18*D xor 0*E = 6*A xor 6*X xor 1*Y xor 1*Z xor 18*E
|
||
3 行目の 4列目は 18なので、18で割る
|
||
|
||
1: 0*A xor 1*B xor 0*C xor 7*D xor 0*E = 2*A xor 3*X xor 1*Y xor 0*Z xor 6*E
|
||
1 行目の 4列目は 7なので、1行目の各値を 7倍したものを XOR する。
|
||
|
||
2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E
|
||
2 行目の 4列目は 6なので、1行目の各値を 6倍したものを XOR する。
|
||
|
||
1: 0*A xor 1*B xor 0*C xor 0*D xor 0*E = 3*A xor 2*X xor 123*Y xor 122*Z xor 1*E
|
||
2: 0*A xor 0*B xor 1*C xor 0*D xor 0*E = 184*A xor 185*X xor 187*Y xor 186*Z xor 1*E
|
||
3: 0*A xor 0*B xor 0*C xor 1*D xor 0*E = 186*A xor 186*X xor 192*Y xor 192*Z xor 1*E
|
||
|
||
左:元の右の行列と同じになってる
|
||
0 1 0 0 0
|
||
0 0 1 0 0
|
||
0 0 0 1 0
|
||
|
||
右:これが失われたソース・ブロックを復元するための行列となる
|
||
3 2 123 122 1
|
||
184 185 187 186 1
|
||
186 186 192 192 1
|
||
*/
|
||
|
||
// 復元用の行列を作る、十分な数のパリティ・ブロックが必要
|
||
int * make_decode_matrix(
|
||
int parity_max, // 本来のパリティ・ブロックの数
|
||
int rows, // 横行、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数
|
||
int cols, // 縦列、行列の横サイズ、本来のソース・ブロック数
|
||
int *exist, // どのブロックが存在するか
|
||
int *id) // 失われたソース・ブロックをどのパリティ・ブロックで代用したか
|
||
{
|
||
int *mat_max, *mat_l, *mat_r;
|
||
int factor, row_start, row_start2, col_find, i, j, k;
|
||
|
||
// 失われたソース・ブロックをどのパリティ・ブロックで代用するか
|
||
j = 0;
|
||
for (i = 0; i < cols; i++){
|
||
id[i] = i;
|
||
if (exist[i] == 0){ // ソース・ブロックが存在しない所
|
||
while (j < parity_max){
|
||
if (exist[cols + j] != 0){ // その番号のパリティ・ブロックが存在すれば
|
||
id[i] = cols + j;
|
||
j++;
|
||
break;
|
||
} else {
|
||
j++;
|
||
}
|
||
}
|
||
if (id[i] < cols){
|
||
printf("\nneed more parity volume\n");
|
||
return NULL; // パリティ・ブロックの数が足りなければ
|
||
}
|
||
}
|
||
}
|
||
|
||
// 全てのパリティ・ブロックが存在すると仮定した行列を作る
|
||
mat_max = make_encode_matrix(parity_max, cols, 0);
|
||
if (mat_max == NULL)
|
||
return NULL;
|
||
|
||
// 存在して利用するパリティ・ブロックだけの行列を作る
|
||
mat_l = malloc(sizeof(int) * rows * cols);
|
||
if (mat_l == NULL) {
|
||
free(mat_max);
|
||
printf("malloc, %d\n", sizeof(int) * rows * cols);
|
||
return NULL;
|
||
}
|
||
j = 0;
|
||
for (i = 0; i < cols; i++){
|
||
if (id[i] >= cols){
|
||
for (k = 0; k < cols; k++)
|
||
mat_l[cols * j + k] = mat_max[cols * (id[i] - cols) + k];
|
||
j++;
|
||
}
|
||
}
|
||
free(mat_max);
|
||
//printf("\nLeft :\n");
|
||
//galois_print_matrix(mat_l, rows, cols);
|
||
|
||
// 失われたソース・ブロックと代用するパリティ・ブロックの位置を表す行列を作る
|
||
mat_r = (int *)calloc(rows * cols, sizeof(int));
|
||
if (mat_r == NULL){
|
||
free(mat_l);
|
||
printf("calloc, %d\n", sizeof(int) * rows * cols);
|
||
return NULL;
|
||
}
|
||
j = 0;
|
||
for (i = 0; i < cols; i++){
|
||
if (id[i] >= cols){
|
||
// j = 行番号
|
||
// id[i] = その行を補ったパリティ・ブロックの番号
|
||
// i = その行のソース・ブロック番号
|
||
mat_r[cols * j + i] = 1;
|
||
j++;
|
||
}
|
||
}
|
||
//printf("\nRight :\n");
|
||
//galois_print_matrix(mat_r, rows, cols);
|
||
|
||
// mat_l の存在するソース・ブロックの列を 0 にするような値で XOR する
|
||
for (i = 0; i < cols; i++){
|
||
if (exist[i] != 0){ // ソース・ブロックが存在する列
|
||
for (j = 0; j < rows; j++){ // 行ごとに
|
||
factor = mat_l[cols * j + i]; // j 行の i 列の値
|
||
mat_l[cols * j + i] = 0; // mat_l[cols * j + i] ^= factor; と同じ
|
||
mat_r[cols * j + i] ^= factor;
|
||
}
|
||
}
|
||
}
|
||
//printf("\nErase source block :\n");
|
||
//galois_print_matrix(mat_l, rows, cols);
|
||
//printf("\n");
|
||
//galois_print_matrix(mat_r, rows, cols);
|
||
|
||
// Gaussian Elimination
|
||
for (j = 0; j < rows; j++){
|
||
row_start = cols * j; // その行の開始位置
|
||
|
||
// mat_l の各行ごとに最初の 0でない値を探す
|
||
for (col_find = 0; col_find < cols; col_find++){
|
||
if (mat_l[row_start + col_find] != 0)
|
||
break;
|
||
}
|
||
if (col_find == cols){ // 見つからなければ、その行列の逆行列を計算できない
|
||
printf("\nmatrix is not invertible\n");
|
||
k = 0;
|
||
for (i = 0; i < cols; i++){
|
||
if (id[i] >= cols){
|
||
if (k == j){
|
||
printf(" parity volume %u is useless\n", (1 + id[i] - cols));
|
||
break;
|
||
}
|
||
k++;
|
||
}
|
||
}
|
||
free(mat_l);
|
||
free(mat_r);
|
||
return NULL;
|
||
}
|
||
factor = mat_l[row_start + col_find]; // col_find 列に 0 ではない値を発見
|
||
if (factor != 1){ // factor が 1でなければ、1にする為に factor で割る
|
||
for (k = 0; k < cols; k++){
|
||
mat_l[row_start + k] = galois_multtable_divide(mat_l[row_start + k], factor);
|
||
mat_r[row_start + k] = galois_multtable_divide(mat_r[row_start + k], factor);
|
||
}
|
||
}
|
||
|
||
// 別の行の同じ col_find 列が 0以外なら、その値を 0にするために、
|
||
// j 行を何倍かしたものを XOR する
|
||
for (i = 0; i < rows; i++){
|
||
if (i == j)
|
||
continue; // 同じ行はとばす
|
||
row_start2 = cols * i; // その行の開始位置
|
||
factor = mat_l[row_start2 + col_find]; // i 行の col_find 列の値
|
||
if (factor != 0){ // 0でなければ
|
||
// 先の計算により、j 行の col_find 列の値は必ず 1なので、この factor が倍率になる
|
||
if (factor == 1){ // 倍率が 1なら、単純に XOR するだけ
|
||
for (k = 0; k < cols; k++){
|
||
mat_l[row_start2 + k] ^= mat_l[row_start + k];
|
||
mat_r[row_start2 + k] ^= mat_r[row_start + k];
|
||
}
|
||
} else {
|
||
for (k = 0; k < cols; k++){
|
||
mat_l[row_start2 + k] ^= galois_multtable_multiply(mat_l[row_start + k], factor);
|
||
mat_r[row_start2 + k] ^= galois_multtable_multiply(mat_r[row_start + k], factor);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//printf("\nDivide & Eliminate :\n");
|
||
//galois_print_matrix(mat_l, rows, cols);
|
||
//printf("\n");
|
||
//galois_print_matrix(mat_r, rows, cols);
|
||
}
|
||
|
||
free(mat_l);
|
||
return mat_r;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
#define CHUNK_SIZE 65536
|
||
|
||
typedef struct {
|
||
HANDLE hFile; // ファイル・ハンドル
|
||
__int64 size; // ファイルの残りサイズ
|
||
} file_ctx;
|
||
|
||
// リード・ソロモン符号を使ってエンコードする
|
||
int rs_encode(
|
||
int source_num, // ソース・ブロックの数
|
||
__int64 block_size, // ブロック・サイズ (最大ファイル・サイズ)
|
||
int parity_num, // パリティ・ブロックの数
|
||
int first_num, // 最初のパリティ・ブロック番号
|
||
file_ctx *files,
|
||
HANDLE *par_hFile,
|
||
PHMD5 *par_md5)
|
||
{
|
||
unsigned char *buffer, *block = NULL;
|
||
int err = 0, i, j;
|
||
int *mat = NULL, factor;
|
||
unsigned int io_size, unit_size, len, rv;
|
||
unsigned int k, offset, length, chunk_count;
|
||
unsigned int time_last, prog_num = 0, prog_base;
|
||
__int64 block_left;
|
||
//unsigned int time1;
|
||
|
||
// 利用できるメモリー量を調べる
|
||
io_size = get_mem_size(block_size * (__int64)(source_num + parity_num));
|
||
if (io_size > 1048576 * 2)
|
||
io_size -= (parity_num * source_num) * 4 + 1048576; // 行列に必要なメモリーとスタック用に 1MB
|
||
if (io_size < 1048576)
|
||
io_size = 1048576;
|
||
io_size /= source_num + 1; // 何個分必要か
|
||
|
||
// ブロック・サイズより大きい、またはブロック・サイズ自体が小さい場合は
|
||
if (((__int64)io_size >= block_size) || (block_size <= 4096)){
|
||
io_size = (unsigned int)block_size; // ブロック・サイズと同じにする
|
||
} else { // ブロック・サイズを 2の乗数サイズの断片に分割する
|
||
// 断片化する場合でもブロック数が多いと 256 * 4096 = 1MB は使う
|
||
__int64 fragment_size = 4096; // 最低サイズ
|
||
while (fragment_size * 2 <= (__int64)io_size)
|
||
fragment_size *= 2;
|
||
io_size = (unsigned int)fragment_size;
|
||
}
|
||
io_size = (io_size + 3) & 0xFFFFFFFC; // 4の倍数にする
|
||
unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす
|
||
prog_base = (unsigned int)((block_size + (__int64)io_size - 1) / (__int64)io_size) * parity_num; // 全体の断片の個数
|
||
//printf("io_size = %d, prog_base = %d\n", io_size, prog_base);
|
||
|
||
block_left = block_size;
|
||
if (galois_create_mult_tables() < 0){
|
||
printf("galois_create_mult_tables\n");
|
||
return 1;
|
||
}
|
||
|
||
// 作業バッファーを確保する
|
||
block = malloc(unit_size * (source_num + 1));
|
||
if (block == NULL){
|
||
printf("malloc, %d\n", unit_size * (source_num + 1));
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
buffer = block + unit_size;
|
||
|
||
// パリティ計算用の行列演算の準備をする
|
||
mat = make_encode_matrix(parity_num, source_num, first_num); // 変換行列
|
||
if (mat == NULL){
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
//galois_print_matrix(mat, parity_num, source_num);
|
||
|
||
// chunk がキャッシュに収まるようにすれば速くなる! (ストリップマイニングという最適化手法)
|
||
// CPU L2キャッシュ・サイズが 256KB として、1/4 なら 64KB
|
||
// chunk サイズが 2**16 なので分割数は 1~65536 の範囲になる
|
||
chunk_count = (unit_size + CHUNK_SIZE - 1) / CHUNK_SIZE;
|
||
//printf("split count = %d, chunk size = %d\n", chunk_count, CHUNK_SIZE);
|
||
|
||
//time1 = GetTickCount();
|
||
// バッファー・サイズごとにパリティ・ブロックを作成する
|
||
time_last = GetTickCount();
|
||
while (block_left > 0){
|
||
// バッファーにソース・ファイルの内容を読み込む
|
||
for (i = 0; i < source_num; i++){
|
||
if (files[i].size > 0){
|
||
if (files[i].size < io_size){
|
||
len = (unsigned int)(files[i].size);
|
||
} else {
|
||
len = io_size;
|
||
}
|
||
files[i].size -= len;
|
||
if (!ReadFile(files[i].hFile, buffer + (unit_size * i), len, &rv, NULL) || (len != rv)){
|
||
print_win32_err();
|
||
printf("ReadFile, data file %d\n", i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
if (len < io_size)
|
||
memset(buffer + (unit_size * i + len), 0, io_size - len);
|
||
// ソース・ブロックのチェックサムを計算する
|
||
checksum4(buffer + (unit_size * i), buffer + (unit_size * i + io_size), io_size);
|
||
} else {
|
||
// ソース・ブロックの値が全て 0 なら、チェックサムも 0 になる。
|
||
memset(buffer + (unit_size * i), 0, unit_size);
|
||
}
|
||
}
|
||
// バッファーに読み込んだサイズ
|
||
if (block_left < io_size){
|
||
len = (unsigned int)block_left;
|
||
} else {
|
||
len = io_size;
|
||
}
|
||
block_left -= len;
|
||
|
||
// パリティ・ブロックごとに
|
||
for (i = 0; i < parity_num; i++){
|
||
/* without strip mining
|
||
memset(block, 0, unit_size); // パリティ・ブロックを 0で埋める
|
||
// ソース・ブロックごとにパリティを追加していく
|
||
for (j = 0; j < source_num; j++){
|
||
factor = mat[i * source_num + j];
|
||
if (factor == 1){
|
||
galois_region_xor(buffer + (unit_size * j), block, unit_size);
|
||
} else if (factor != 0){
|
||
galois_region_multiply(buffer + (unit_size * j), block, unit_size, factor);
|
||
}
|
||
}
|
||
*/
|
||
length = CHUNK_SIZE;
|
||
for (k = 0; k < chunk_count; k++){ // chunk の番号
|
||
offset = k * CHUNK_SIZE;
|
||
if (offset + length >= unit_size){ // 最後の chunk なら
|
||
length = unit_size - offset;
|
||
k = chunk_count;
|
||
}
|
||
|
||
memset(block + offset, 0, length); // パリティ・ブロックを 0で埋める
|
||
// ソース・ブロックごとにパリティを追加していく
|
||
for (j = 0; j < source_num; j++){
|
||
factor = mat[i * source_num + j];
|
||
if (factor == 1){
|
||
galois_region_xor(buffer + (unit_size * j + offset), block + offset, length);
|
||
} else if (factor != 0){
|
||
galois_region_multiply(buffer + (unit_size * j + offset), block + offset, length, factor);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 経過表示
|
||
prog_num++;
|
||
if (GetTickCount() - time_last >= UPDATE_TIME){
|
||
if (print_progress((prog_num * 1000) / prog_base)){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
time_last = GetTickCount();
|
||
}
|
||
|
||
// パリティ・ブロックのチェックサムを検証する
|
||
checksum4(block, (unsigned char *)&rv, io_size);
|
||
if (memcmp(block + io_size, &rv, HASH_SIZE) != 0){
|
||
printf("checksum mismatch, parity volume %d\n", i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
Phmd5Process(&(par_md5[i]), block, len); // ハッシュ値を計算する
|
||
// リカバリ・ファイルへ書き込む
|
||
if (!WriteFile(par_hFile[i], block, len, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, parity volume %d\n", first_num + 1 + i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
}
|
||
}
|
||
print_progress_done(); // 改行して行の先頭に戻しておく
|
||
//time1 = GetTickCount() - time1;
|
||
//printf("encode %u.%03u sec\n", time1 / 1000, time1 % 1000);
|
||
|
||
error_end:
|
||
if (block)
|
||
free(block);
|
||
if (mat)
|
||
free(mat);
|
||
// gflib のテーブルを解放する
|
||
galois_free_tables();
|
||
return err;
|
||
}
|
||
|
||
// リード・ソロモン符号を使ってデコードする
|
||
int rs_decode(
|
||
int source_num, // 本来のソース・ブロックの数
|
||
int block_lost, // 失われたソース・ブロックの数
|
||
__int64 block_size, // ブロック・サイズ (最大ファイル・サイズ)
|
||
int parity_max, // 本来のパリティ・ブロックの数
|
||
int *exist, // そのブロックが存在するか
|
||
file_ctx *files)
|
||
{
|
||
unsigned char *buffer, *block = NULL;
|
||
int err = 0, i, j;
|
||
int *mat = NULL, *id = NULL, factor;
|
||
int block_recover;
|
||
unsigned int io_size, unit_size, len, rv, len2;
|
||
unsigned int k, offset, length, chunk_count;
|
||
unsigned int time_last, prog_num = 0, prog_base;
|
||
__int64 block_left;
|
||
|
||
// 利用できるメモリー量を調べる
|
||
io_size = get_mem_size(block_size * (__int64)(source_num + block_lost));
|
||
io_size -= (block_lost * source_num * 8) + (parity_max * source_num * 4) + (source_num * 4);
|
||
io_size -= 1048576; // 行列に必要なメモリーとスタック用に 1MB
|
||
if (io_size < 1048576)
|
||
io_size = 1048576;
|
||
io_size /= source_num + 1; // 何個分必要か
|
||
|
||
// ブロック・サイズより大きい、またはブロック・サイズ自体が小さい場合は
|
||
if (((__int64)io_size >= block_size) || (block_size <= 4096)){
|
||
io_size = (unsigned int)block_size; // ブロック・サイズと同じにする
|
||
} else { // ブロック・サイズを 2の乗数サイズの断片に分割する
|
||
// 断片化する場合でもブロック数が多いと 256 * 4096 = 1MB は使う
|
||
__int64 fragment_size = 4096; // 最低サイズ
|
||
while (fragment_size * 2 <= (__int64)io_size)
|
||
fragment_size *= 2;
|
||
io_size = (unsigned int)fragment_size;
|
||
}
|
||
io_size = (io_size + 3) & 0xFFFFFFFC; // 4の倍数にする
|
||
unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす
|
||
prog_base = (unsigned int)((block_size + (__int64)io_size - 1) / (__int64)io_size) * block_lost; // 全体の断片の個数
|
||
//printf("io_size = %d\n", io_size);
|
||
|
||
block_left = block_size;
|
||
if (galois_create_mult_tables() < 0){
|
||
printf("galois_create_mult_tables\n");
|
||
return 1;
|
||
}
|
||
|
||
// 作業バッファーを確保する
|
||
block = malloc(unit_size * (source_num + 1));
|
||
if (block == NULL){
|
||
printf("malloc, %d\n", unit_size * (source_num + 1));
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
buffer = block + unit_size;
|
||
|
||
// パリティ計算用の行列演算の準備をする
|
||
id = malloc(sizeof(int) * source_num);
|
||
if (id == NULL){
|
||
printf("malloc, %d\n", sizeof(int) * source_num);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
mat = make_decode_matrix(parity_max, block_lost, source_num, exist, id);
|
||
if (mat == NULL){
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
//galois_print_matrix(mat, block_lost, source_num);
|
||
//for (i = 0; i < source_num; i++)
|
||
// printf("id[%d] = %d\n", i, id[i]);
|
||
|
||
// chunk サイズが 2**16 なので分割数は 1~65536 の範囲になる
|
||
chunk_count = (unit_size + CHUNK_SIZE - 1) / CHUNK_SIZE;
|
||
|
||
// バッファー・サイズごとにソース・ブロックを復元する
|
||
time_last = GetTickCount();
|
||
while (block_left > 0){
|
||
// バッファーにソース・ファイルとリカバリ・ファイルの内容を読み込む
|
||
for (i = 0; i < source_num; i++){
|
||
//printf("%d: id = %d, exist = %d, size = %I64u \n", i, id[i], exist[id[i]], files[id[i]].size);
|
||
if (files[id[i]].size > 0){
|
||
if (files[id[i]].size < io_size){
|
||
len = (unsigned int)(files[id[i]].size);
|
||
} else {
|
||
len = io_size;
|
||
}
|
||
files[id[i]].size -= len;
|
||
if (!ReadFile(files[id[i]].hFile, buffer + (unit_size * i), len, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("ReadFile, data file %d\n", id[i]);
|
||
err = 1;
|
||
goto error_end;
|
||
} else if (len != rv){
|
||
printf("ReadFile, data file %d, %d, %d\n", id[i], len, rv);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
if (len < io_size)
|
||
memset(buffer + (unit_size * i + len), 0, io_size - len);
|
||
// ソース・ブロックのチェックサムを計算する
|
||
checksum4(buffer + (unit_size * i), buffer + (unit_size * i + io_size), io_size);
|
||
} else {
|
||
// ソース・ブロックの値が全て 0 なら、チェックサムも 0 になる。
|
||
memset(buffer + (unit_size * i), 0, unit_size);
|
||
}
|
||
}
|
||
// バッファーに読み込んだサイズ
|
||
if (block_left < io_size){
|
||
len = (unsigned int)block_left;
|
||
} else {
|
||
len = io_size;
|
||
}
|
||
block_left -= len;
|
||
|
||
// 失われたソース・ブロックごとに
|
||
block_recover = 0;
|
||
for (i = 0; i < source_num; i++){
|
||
if (id[i] >= source_num){ // パリティ・ブロックで補った部分
|
||
/* without strip mining
|
||
memset(block, 0, unit_size); // ソース・ブロックを 0で埋める
|
||
// 失われたソース・ブロックを復元していく
|
||
for (j = 0; j < source_num; j++) {
|
||
factor = mat[source_num * block_recover + j];
|
||
if (factor == 1){
|
||
galois_region_xor(buffer + (unit_size * j), block, unit_size);
|
||
} else if (factor != 0){
|
||
galois_region_multiply(buffer + (unit_size * j), block, unit_size, factor);
|
||
}
|
||
}
|
||
*/
|
||
length = CHUNK_SIZE;
|
||
for (k = 0; k < chunk_count; k++){ // chunk の番号
|
||
offset = k * CHUNK_SIZE;
|
||
if (offset + length >= unit_size){ // 最後の chunk なら
|
||
length = unit_size - offset;
|
||
k = chunk_count;
|
||
}
|
||
|
||
memset(block + offset, 0, length); // ソース・ブロックを 0で埋める
|
||
// 失われたソース・ブロックを復元していく
|
||
for (j = 0; j < source_num; j++) {
|
||
factor = mat[source_num * block_recover + j];
|
||
if (factor == 1){
|
||
galois_region_xor(buffer + (unit_size * j + offset), block + offset, length);
|
||
} else if (factor != 0){
|
||
galois_region_multiply(buffer + (unit_size * j + offset), block + offset, length, factor);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 経過表示
|
||
prog_num++;
|
||
if (GetTickCount() - time_last >= UPDATE_TIME){
|
||
if (print_progress((prog_num * 1000) / prog_base)){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
time_last = GetTickCount();
|
||
}
|
||
|
||
// 復元されたソース・ブロックのチェックサムを検証する
|
||
checksum4(block, (unsigned char *)&rv, io_size);
|
||
if (memcmp(block + io_size, &rv, HASH_SIZE) != 0){
|
||
printf("checksum mismatch, recovered data file %d\n", i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
block_recover++;
|
||
// ソース・ファイルに書き込むサイズ
|
||
if (files[i].size < len){
|
||
len2 = (unsigned int)(files[i].size);
|
||
} else {
|
||
len2 = len;
|
||
}
|
||
files[i].size -= len2;
|
||
// ソース・ファイルへ書き込む
|
||
if (!WriteFile(files[i].hFile, block, len2, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, data file %d\n", i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
print_progress_done(); // 改行して行の先頭に戻しておく
|
||
|
||
error_end:
|
||
if (block)
|
||
free(block);
|
||
if (id)
|
||
free(id);
|
||
if (mat)
|
||
free(mat);
|
||
// gflib のテーブルを解放する
|
||
galois_free_tables();
|
||
return err;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
// PAR 1.0 のヘッダーを作成する
|
||
int set_header(
|
||
unsigned char *buf,
|
||
file_ctx *files, // ソース・ブロックのハンドルとサイズ
|
||
int source_num, // ソース・ブロックの数
|
||
int file_num, // ソース・ファイルの数
|
||
wchar_t *list_buf, // ソース・ファイルのリスト
|
||
int list_len) // ファイル・リストの文字数
|
||
{
|
||
unsigned char hash[16], hash16[16], set_hash[16 * 256];
|
||
unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
|
||
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e};
|
||
wchar_t file_path[MAX_LEN], *file_name;
|
||
int i, off, list_off, len, num = 0;
|
||
__int64 file_size, total_file_size, prog_now;
|
||
WIN32_FILE_ATTRIBUTE_DATA AttrData;
|
||
|
||
memset(buf, 0, 96);
|
||
// Identification String
|
||
buf[0] = 'P';
|
||
buf[1] = 'A';
|
||
buf[2] = 'R';
|
||
// Version Number
|
||
i = 0x00010000; // PAR 1.0
|
||
memcpy(buf + 8, &i, 4);
|
||
// "program"-"version"-"subversion"-"subsubversion".
|
||
buf[12] = PRODUCT_VERSION & 0xF; // subsubversion
|
||
buf[13] = (PRODUCT_VERSION >> 4) & 0xF; // subversion
|
||
buf[14] = PRODUCT_VERSION >> 8; // version, ちなみに par-cmdline の最終バージョンは 0.9.0 ?
|
||
buf[15] = 2; // program, 1=Mirror, 2=PAR, 3=SmartPar, 0&4~=Unknown client
|
||
// control hash の 16バイトは最後に書き込む
|
||
// set hash の 16バイトは後で書き込む
|
||
// volume number はパリティ・ブロックごとに変更する
|
||
// number of files
|
||
memcpy(buf + 56, &file_num, 4);
|
||
// start offset of the file list
|
||
buf[64] = 0x60;
|
||
// file list size の 8バイトは後で書き込む
|
||
// start offset of the data の 8バイトは後で書き込む
|
||
// data size はパリティ・ブロックならブロック・サイズを書き込む
|
||
|
||
// 合計ファイル・サイズを求める
|
||
total_file_size = 0;
|
||
prog_now = 0;
|
||
list_off = 0;
|
||
while (list_off < list_len){
|
||
// ファイル名を取得する
|
||
file_name = list_buf + list_off; // サブ・ディレクトリを含まないのでそのまま使う
|
||
list_off += wcslen(file_name) + 1;
|
||
|
||
wcscpy(file_path, base_dir);
|
||
wcscat(file_path, file_name);
|
||
if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){
|
||
print_win32_err();
|
||
printf_cp("cannot open input file, %s\n", file_name);
|
||
return 0;
|
||
}
|
||
file_size = ((__int64)(AttrData.nFileSizeHigh) << 32) | (__int64)(AttrData.nFileSizeLow);
|
||
total_file_size += file_size;
|
||
}
|
||
|
||
// ここから file list
|
||
off = 0x0060;
|
||
list_off = 0;
|
||
while (list_off < list_len){
|
||
// ファイル名を取得する
|
||
file_name = list_buf + list_off; // サブ・ディレクトリを含まないのでそのまま使う
|
||
len = wcslen(file_name);
|
||
list_off += (len + 1);
|
||
len *= 2; // 文字数からバイト数にする
|
||
|
||
// entry size
|
||
i = 0x38 + len;
|
||
memcpy(buf + off, &i, 4);
|
||
memset(buf + off + 4, 0, 4);
|
||
off += 8;
|
||
// status field
|
||
wcscpy(file_path, base_dir);
|
||
wcscat(file_path, file_name);
|
||
if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){
|
||
print_win32_err();
|
||
printf_cp("cannot open input file, %s\n", file_name);
|
||
return 0;
|
||
}
|
||
file_size = ((__int64)(AttrData.nFileSizeHigh) << 32) | (__int64)(AttrData.nFileSizeLow);
|
||
if (file_size > 0){
|
||
i = 1;
|
||
} else {
|
||
i = 0; // 空のファイルはパリティ計算に含めない
|
||
}
|
||
memcpy(buf + off, &i, 4);
|
||
memset(buf + off + 4, 0, 4);
|
||
off += 8;
|
||
// size
|
||
memcpy(buf + off, &file_size, 8);
|
||
off += 8;
|
||
// MD5 hash
|
||
if (file_size > 0){
|
||
// パリティ・ブロック計算用に開く
|
||
files[num].size = file_size;
|
||
files[num].hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (files[num].hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf_cp("cannot open input file, %s\n", file_name);
|
||
return 0;
|
||
}
|
||
// MD5 を計算する
|
||
i = file_md5_total(files[num].hFile, file_size, hash, hash16, total_file_size, &prog_now);
|
||
if (i != 0){
|
||
if (i < 0)
|
||
return i;
|
||
printf_cp("file_md5_total, %s\n", file_name);
|
||
return 0;
|
||
}
|
||
} else {
|
||
memcpy(hash, hash0, 16);
|
||
}
|
||
memcpy(buf + off, hash, 16);
|
||
off += 16;
|
||
if (file_size > 0)
|
||
memcpy(set_hash + (num * 16), hash, 16);
|
||
// 16k MD5 hash
|
||
if (file_size > 16384)
|
||
memcpy(hash, hash16, 16);
|
||
memcpy(buf + off, hash, 16);
|
||
off += 16;
|
||
// filename
|
||
memcpy(buf + off, file_name, len);
|
||
off += len;
|
||
|
||
if (file_size > 0)
|
||
num++;
|
||
}
|
||
|
||
// set hash をここで書き込む
|
||
data_md5(set_hash, source_num * 16, hash);
|
||
memcpy(buf + 32, hash, 16);
|
||
// file list size をここで書き込む
|
||
i = off - 96;
|
||
memcpy(buf + 72, &i, 4);
|
||
// start offset of the data をここで書き込む
|
||
memcpy(buf + 80, &off, 4);
|
||
|
||
// 最後にファイルのハッシュ値を計算する
|
||
data_md5(buf + 32, off - 32, hash);
|
||
memcpy(buf + 16, hash, 16);
|
||
|
||
return off;
|
||
}
|
||
|
||
// インデックス・ファイルを作り直す
|
||
int recreate_par(
|
||
wchar_t *file_path, // PAR ファイルのパス
|
||
unsigned char *pxx_buf, // PXX ファイルのヘッダー
|
||
unsigned char *buf) // 作業バッファー
|
||
{
|
||
unsigned char hash[16];
|
||
int data_off, rv;
|
||
HANDLE hFileWrite;
|
||
|
||
// start offset of the data を読み取る
|
||
memcpy(&data_off, pxx_buf + 80, 4);
|
||
memcpy(buf, pxx_buf, data_off); // ヘッダー部分をコピーする
|
||
|
||
// volume number を 0にする
|
||
memset(buf + 48, 0, 8);
|
||
// data size を 0にする
|
||
memset(buf + 88, 0, 8);
|
||
// control hash を計算しなおす
|
||
data_md5(buf + 32, data_off - 32, hash);
|
||
memcpy(buf + 16, hash, 16);
|
||
|
||
// ファイルに書き込む
|
||
hFileWrite = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFileWrite == INVALID_HANDLE_VALUE)
|
||
return 1;
|
||
if (!WriteFile(hFileWrite, buf, data_off, &rv, NULL)){
|
||
CloseHandle(hFileWrite);
|
||
return 1;
|
||
}
|
||
CloseHandle(hFileWrite);
|
||
return 0;
|
||
}
|
||
|
||
// PAR 1.0 のヘッダーから値を読み取る
|
||
int get_header(
|
||
wchar_t *file_name,
|
||
HANDLE hFile,
|
||
unsigned char *buf,
|
||
int buf_size,
|
||
unsigned char *set_hash, // NULL でなければ set hash、同じ構成の PXX かどうかを識別するため
|
||
int *volume_num, // リカバリ・ファイルの番号
|
||
int *file_num, // ソース・ファイルの数
|
||
int *list_off, // ソース・ファイルのリストの開始位置
|
||
int *list_size, // ソース・ファイルのリストのサイズ
|
||
int *data_off, // ソースの開始位置
|
||
__int64 *data_size) // ソースのサイズ
|
||
{
|
||
unsigned char hash[16];
|
||
int i;
|
||
unsigned int rv, meta_data[7];
|
||
|
||
if (buf_size <= 96)
|
||
return 1;
|
||
|
||
// ファイルの先頭に戻す
|
||
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){
|
||
print_win32_err();
|
||
return 1;
|
||
}
|
||
|
||
// ヘッダー部分を読み込む
|
||
if (!ReadFile(hFile, buf, buf_size, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("ReadFile, header\n");
|
||
return 1;
|
||
}
|
||
|
||
// Identification String
|
||
memcpy(&i, buf, 4);
|
||
if (i != 5390672) // 'P' + 'A' * 256 + 'R' * 65536
|
||
return 1;
|
||
memcpy(&i, buf + 4, 4);
|
||
if (i != 0)
|
||
return 1;
|
||
// Version Number
|
||
memcpy(&i, buf + 8, 4);
|
||
if ((i & 0xffff0000) != 0x00010000) // PAR v1.** にだけ対応する
|
||
return 1;
|
||
|
||
// 完全なリカバリ・ファイルを見つけた後だけ、検査結果を探す
|
||
if (list_size == NULL){
|
||
i = check_ini_state(0x10FF, meta_data, hFile);
|
||
if (i == -2){ // 記録が無い、または状態が変化してる
|
||
// control hash
|
||
if (rv = file_md5_from32(file_name, hFile, hash))
|
||
return rv;
|
||
i = 0;
|
||
if (memcmp(hash, buf + 16, 16) != 0)
|
||
i = 1;
|
||
// 今回の検査結果を書き込む
|
||
write_ini_state(0x10FF, meta_data, i);
|
||
}
|
||
if (i != 0)
|
||
return 1;
|
||
|
||
} else {
|
||
// control hash
|
||
if (rv = file_md5_from32(file_name, hFile, hash))
|
||
return rv;
|
||
if (memcmp(hash, buf + 16, 16) != 0)
|
||
return 1;
|
||
}
|
||
|
||
// set hash
|
||
if (set_hash != NULL)
|
||
memcpy(set_hash, buf + 32, 16);
|
||
// volume number
|
||
if (volume_num != NULL)
|
||
memcpy(volume_num, buf + 48, 4);
|
||
// number of files
|
||
if (file_num != NULL)
|
||
memcpy(file_num, buf + 56, 4);
|
||
// start offset of the file list
|
||
if (list_off != NULL)
|
||
memcpy(list_off, buf + 64, 4);
|
||
// file list size
|
||
if (list_size != NULL)
|
||
memcpy(list_size, buf + 72, 4);
|
||
// start offset of the data
|
||
if (data_off != NULL)
|
||
memcpy(data_off, buf + 80, 4);
|
||
// data size
|
||
if (data_size != NULL)
|
||
memcpy(data_size, buf + 88, 8);
|
||
|
||
return 0;
|
||
}
|
||
|
||
// ファイル・リストからブロック数を読み取る
|
||
int get_source_num(
|
||
unsigned char *buf,
|
||
__int64 *block_size)
|
||
{
|
||
int i, off, file_num, entry_size, status, source_num = 0;
|
||
__int64 file_size;
|
||
|
||
// number of files
|
||
memcpy(&file_num, buf + 56, 4);
|
||
// start offset of the file list
|
||
memcpy(&off, buf + 64, 4);
|
||
|
||
for (i = 0; i < file_num; i++){
|
||
// entry size
|
||
memcpy(&entry_size, buf + off, 4);
|
||
// status field
|
||
memcpy(&status, buf + (off + 8), 4);
|
||
if ((status & 1) == 1){
|
||
source_num++;
|
||
// file size
|
||
memcpy(&file_size, buf + (off + 16), 8);
|
||
if (file_size > *block_size)
|
||
*block_size = file_size;
|
||
}
|
||
|
||
off += entry_size;
|
||
}
|
||
|
||
printf("%3d \r", source_num);
|
||
return source_num;
|
||
}
|
||
|
||
// コメントを読み取る
|
||
static void read_comment(
|
||
wchar_t *par_comment,
|
||
unsigned char *buf,
|
||
int len)
|
||
{
|
||
if (par_comment[0] != 0)
|
||
return;
|
||
|
||
if (len >= COMMENT_LEN * 2)
|
||
len = (COMMENT_LEN - 1) * 2;
|
||
memcpy(par_comment, buf, len);
|
||
len /= 2; // 文字数にする
|
||
par_comment[len] = 0; // 末尾を null 文字にする
|
||
for (len = 0; len < COMMENT_LEN; len++){ // 表示する前に sanitalize する
|
||
if (par_comment[len] == 0){
|
||
break;
|
||
} else if ((par_comment[len] <= 31) || (par_comment[len] == 127)){ // 制御文字を消す
|
||
par_comment[len] = ' ';
|
||
}
|
||
}
|
||
}
|
||
|
||
// リカバリ・ファイルを作成する
|
||
int par1_create(
|
||
int switch_p, // インデックス・ファイルを作らない
|
||
int source_num, // ソース・ブロックの数
|
||
__int64 block_size, // ブロック・サイズ (最大ファイル・サイズ)
|
||
int parity_num, // パリティ・ブロックの数
|
||
int first_vol, // 最初のリカバリ・ファイル番号
|
||
int file_num, // ソース・ファイルの数
|
||
wchar_t *list_buf, // ソース・ファイルのリスト
|
||
int list_len, // ファイル・リストの文字数
|
||
wchar_t *par_comment) // コメント
|
||
{
|
||
unsigned char *header_buf = NULL;
|
||
wchar_t recovery_base[MAX_LEN];
|
||
int err = 0, i;
|
||
int int4, buf_size;
|
||
unsigned int rv;
|
||
HANDLE *par_hFile = NULL;
|
||
file_ctx *files = NULL;
|
||
PHMD5 *par_md5 = NULL;
|
||
|
||
buf_size = 0x0060 + (0x0038 * file_num) + (list_len * 2); // 最大サイズ分確保しておく
|
||
header_buf = malloc(buf_size);
|
||
if (header_buf == NULL){
|
||
printf("malloc, %d\n", buf_size);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
files = malloc(sizeof(file_ctx) * source_num);
|
||
if (files == NULL){
|
||
printf("malloc, %d\n", sizeof(file_ctx) * source_num);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
for (i = 0; i < source_num; i++)
|
||
files[i].hFile = NULL;
|
||
|
||
int4 = sizeof(HANDLE) * parity_num;
|
||
if (int4 == 0)
|
||
int4 = sizeof(HANDLE);
|
||
par_hFile = malloc(int4);
|
||
if (par_hFile == NULL){
|
||
printf("malloc, %d\n", int4);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
par_hFile[0] = NULL;
|
||
for (i = 1; i < parity_num; i++)
|
||
par_hFile[i] = NULL;
|
||
|
||
if (parity_num > 0){
|
||
par_md5 = malloc(sizeof(PHMD5) * parity_num);
|
||
if (par_md5 == NULL){
|
||
printf("malloc, %d\n", sizeof(PHMD5) * parity_num);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
for (i = 0; i < parity_num; i++)
|
||
Phmd5Begin(&(par_md5[i]));
|
||
}
|
||
|
||
printf("\n");
|
||
print_progress_text(0, "Computing file hash");
|
||
|
||
// ヘッダーを作成する
|
||
buf_size = set_header(header_buf, files, source_num, file_num, list_buf, list_len);
|
||
if (buf_size <= 0){
|
||
if (buf_size < 0){
|
||
err = 2;
|
||
} else {
|
||
err = 1;
|
||
}
|
||
goto error_end;
|
||
}
|
||
|
||
if (switch_p == 0){ // インデックス・ファイルを作るなら
|
||
// コメントがあるならヘッダーを修正する
|
||
if (int4 = wcslen(par_comment) * 2)
|
||
memcpy(header_buf + 88, &int4, 4);
|
||
|
||
// リカバリ・ファイル (*.PAR) を書き込む
|
||
par_hFile[0] = CreateFile(recovery_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (par_hFile[0] == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf_cp("cannot create file, %s\n", recovery_file);
|
||
par_hFile[0] = NULL;
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
if (!WriteFile(par_hFile[0], header_buf, buf_size, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, index file\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// コメントがあるならコメントも書き込む
|
||
if (int4 = wcslen(par_comment) * 2){
|
||
if (!WriteFile(par_hFile[0], par_comment, int4, &int4, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, index file\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// MD5を計算しなおす
|
||
if (err = file_md5_from32(NULL, par_hFile[0], header_buf + 16)){
|
||
if (err == 1)
|
||
printf("file_md5_from32\n");
|
||
goto error_end;
|
||
}
|
||
if (SetFilePointer(par_hFile[0], 16, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
if (!WriteFile(par_hFile[0], header_buf + 16, 16, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, index file\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
}
|
||
CloseHandle(par_hFile[0]);
|
||
par_hFile[0] = NULL;
|
||
}
|
||
print_progress_done();
|
||
if (parity_num == 0) // パリティ・ブロックを作らない場合はここで終わる
|
||
goto creation_end;
|
||
|
||
// ソース・ファイルから読み込む準備をする
|
||
for (i = 0; i < source_num; i++){
|
||
if (SetFilePointer(files[i].hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
}
|
||
|
||
// リカバリ・ファイル (*.PXX) の名前の基
|
||
wcscpy(recovery_base, recovery_file);
|
||
int4 = wcslen(recovery_base);
|
||
recovery_base[int4 - 1] = 0;
|
||
recovery_base[int4 - 2] = 0;
|
||
print_progress_text(0, "Creating parity volume");
|
||
|
||
// リカバリ・ファイル (*.PXX) に書き込む準備をする
|
||
memcpy(header_buf + 88, &block_size, 8); // ブロック・サイズにする
|
||
for (i = 0; i < parity_num; i++){
|
||
int4 = first_vol + i; // 作成する parity volume の番号にする
|
||
swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, int4);
|
||
par_hFile[i] = CreateFile(recovery_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (par_hFile[i] == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
par_hFile[i] = NULL;
|
||
printf_cp("cannot create file, %s\n", recovery_file);
|
||
|
||
// これまでに作ったリカバリ・ファイルを削除する
|
||
if (switch_p == 0){
|
||
wcscpy(recovery_file, recovery_base);
|
||
wcscat(recovery_file, L"ar");
|
||
DeleteFile(recovery_file);
|
||
}
|
||
for (int4 = 0; int4 < i; int4++){
|
||
CloseHandle(par_hFile[int4]);
|
||
par_hFile[int4] = NULL;
|
||
swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, first_vol + int4);
|
||
DeleteFile(recovery_file); // 途中までのリカバリ・ファイルを削除する
|
||
}
|
||
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// ヘッダーを書き込む
|
||
memcpy(header_buf + 48, &int4, 2); // volume number にする
|
||
if (!WriteFile(par_hFile[i], header_buf, buf_size, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, parity volume %d\n", int4);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// ヘッダー部分からのハッシュ値を計算しかけておく
|
||
Phmd5Process(&(par_md5[i]), header_buf + 32, buf_size - 32);
|
||
}
|
||
|
||
// パリティ・ブロックを作成する
|
||
if (err = rs_encode(source_num, block_size, parity_num, first_vol - 1, files, par_hFile, par_md5)){
|
||
if (err == 2){ // キャンセルされたのなら、作業中のファイルを削除する
|
||
if (switch_p == 0){
|
||
wcscpy(recovery_file, recovery_base);
|
||
wcscat(recovery_file, L"ar");
|
||
DeleteFile(recovery_file);
|
||
}
|
||
for (i = 0; i < parity_num; i++){
|
||
CloseHandle(par_hFile[i]);
|
||
par_hFile[i] = NULL;
|
||
swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, first_vol + i);
|
||
DeleteFile(recovery_file); // 途中までのリカバリ・ファイルを削除する
|
||
}
|
||
}
|
||
goto error_end;
|
||
}
|
||
|
||
// リカバリ・ファイル (*.PXX) の MD5を計算しなおす
|
||
for (i = 0; i < parity_num; i++){
|
||
Phmd5End(&(par_md5[i])); // ハッシュ値の計算終了
|
||
memcpy(header_buf + 16, par_md5[i].hash, 16);
|
||
if (SetFilePointer(par_hFile[i], 16, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
if (!WriteFile(par_hFile[i], header_buf + 16, 16, &rv, NULL)){
|
||
print_win32_err();
|
||
printf("WriteFile, parity volume %d\n", i);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
}
|
||
|
||
creation_end:
|
||
printf("\nCreated successfully\n");
|
||
error_end:
|
||
if (list_buf)
|
||
free(list_buf);
|
||
if (header_buf)
|
||
free(header_buf);
|
||
if (par_md5)
|
||
free(par_md5);
|
||
if (files){
|
||
for (i = 0; i < source_num; i++){
|
||
if (files[i].hFile != NULL)
|
||
CloseHandle(files[i].hFile);
|
||
}
|
||
free(files);
|
||
}
|
||
if (par_hFile){
|
||
for (i = 0; i < parity_num; i++){
|
||
if (par_hFile[i] != NULL)
|
||
CloseHandle(par_hFile[i]);
|
||
}
|
||
free(par_hFile);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
// ソース・ファイルの破損や欠損を調べる
|
||
int par1_verify(
|
||
int switch_b, // 既存のファイルを別名にしてどかす
|
||
int switch_p, // インデックス・ファイルを作り直す
|
||
wchar_t *par_comment) // コメント
|
||
{
|
||
char ascii_buf[MAX_LEN * 3], ascii_buf2[MAX_LEN * 3];
|
||
unsigned char *header_buf = NULL, *header_buf2 = NULL, hash[16], set_hash[16];
|
||
wchar_t file_name[MAX_LEN], file_path[MAX_LEN], find_path[MAX_LEN];
|
||
wchar_t par_char[] = L"AR";
|
||
int err = 0, i, j, bad_flag, find_flag, parity_flag, par_flag, exist[99];
|
||
int len, dir_len, base_len, buf_size, entry_size, entry_size2, blk;
|
||
int volume_num, volume_max, file_num, list_off, list_size, data_off;
|
||
int buf_size2, volume_num2, file_num2, list_off2, data_off2;
|
||
int parity_num, source_num, block_lost = 0, need_repair = 0, recovery_lost = 0;
|
||
unsigned int rv, meta_data[7];
|
||
__int64 file_size, file_size2, data_size, block_size = 0, total_file_size;
|
||
HANDLE hFile = NULL, hFind;
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
// リカバリ・ファイルの名前
|
||
wcscpy(file_path, recovery_file);
|
||
len = wcslen(file_path);
|
||
file_path[len - 2] = 0; // 末尾の2文字を消去する
|
||
if (file_path[len - 3] == 'p'){
|
||
par_char[0] = 'a';
|
||
par_char[1] = 'r';
|
||
}
|
||
// ディレクトリの長さ
|
||
dir_len = len - 3;
|
||
while (dir_len >= 0){
|
||
if ((file_path[dir_len] == '\\') || (file_path[dir_len] == '/'))
|
||
break;
|
||
dir_len--;
|
||
}
|
||
dir_len++;
|
||
base_len = wcslen(base_dir);
|
||
|
||
// リカバリ・ファイルの一覧を表示する
|
||
printf("PAR File list :\n");
|
||
printf(" Size : Filename\n");
|
||
total_file_size = 0;
|
||
parity_num = 0;
|
||
volume_max = 0;
|
||
for (i = 0; i <= 99; i++){
|
||
if (i == 0){
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
blk = 0;
|
||
} else {
|
||
swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i);
|
||
blk = 1;
|
||
}
|
||
// ファイルを開くことができるか、サイズを取得できるかを確かめる
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
continue;
|
||
}
|
||
// ファイルのサイズを取得する
|
||
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
|
||
CloseHandle(hFile);
|
||
continue;
|
||
}
|
||
CloseHandle(hFile);
|
||
total_file_size += file_size;
|
||
parity_num++;
|
||
volume_max = i;
|
||
// リカバリ・ファイルの名前
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
printf("%13I64d : \"%s\"\n", file_size, ascii_buf);
|
||
}
|
||
hFile = NULL;
|
||
printf("\nPAR File total size\t: %I64d\n", total_file_size);
|
||
printf("PAR File possible count\t: %d\n\n", parity_num);
|
||
|
||
// 指定されたリカバリ・ファイルを開く
|
||
hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf("valid file is not found\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// ファイルのサイズを取得する
|
||
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// ヘッダー用のバッファーを確保する
|
||
buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ
|
||
if ((__int64)buf_size > file_size)
|
||
buf_size = (unsigned int)file_size;
|
||
header_buf = malloc(buf_size);
|
||
if (header_buf == NULL){
|
||
printf("malloc, %d\n", buf_size);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// PAR や PXX ファイルとして正しいかを調べる
|
||
par_flag = 0;
|
||
par_comment[0] = 0;
|
||
for (i = 0; i < 99; i++)
|
||
exist[i] = -1;
|
||
if (rv = get_header(NULL, hFile, header_buf, buf_size, set_hash,
|
||
&volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
volume_num = -1;
|
||
par_flag |= 2;
|
||
} else {
|
||
// 各ブロックが存在するかを記録していく
|
||
j = get_source_num(header_buf, &block_size);
|
||
check_ini_file(set_hash, j); // 前回の検査結果が存在するかどうか
|
||
if (volume_num == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size < (int)data_size)
|
||
rv = buf_size;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf + data_off, rv);
|
||
}
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
|
||
// 他のリカバリ・ファイルのヘッダー用のバッファーを確保する
|
||
if (volume_num == -1){
|
||
buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ
|
||
} else {
|
||
buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * file_num);
|
||
}
|
||
header_buf2 = malloc(buf_size2);
|
||
if (header_buf2 == NULL){
|
||
printf("malloc, %d\n", buf_size2);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// 他のリカバリ・ファイルも調べる
|
||
printf("Loading PAR File:\n");
|
||
printf(" Block Status : Filename\n");
|
||
fflush(stdout);
|
||
parity_num = 0;
|
||
for (i = 0; i <= 99; i++){
|
||
if (err = cancel_progress()) // キャンセル処理
|
||
goto error_end;
|
||
if (i == 0){
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
blk = 0;
|
||
} else {
|
||
swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i);
|
||
blk = 1;
|
||
}
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
|
||
// 既にチェック済みなら
|
||
if ((_wcsicmp(recovery_file + dir_len, find_path + dir_len) == 0) && (volume_num != -1)){
|
||
if ((volume_num > 0) && (volume_num <= 99)){
|
||
if (exist[volume_num - 1] == -1) // 係数が重複してるのは使えない
|
||
parity_num++;
|
||
exist[volume_num - 1] = i;
|
||
if (volume_max < volume_num)
|
||
volume_max = volume_num;
|
||
}
|
||
if ((par_flag & 2) != 0){
|
||
printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf);
|
||
recovery_lost++;
|
||
} else if (i == volume_num){
|
||
printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf);
|
||
} else {
|
||
blk = (volume_num > 0) ? 1 : 0;
|
||
printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// ファイルを開く
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE)
|
||
continue;
|
||
// PAR や PXX ファイルとして正しいかを調べる
|
||
bad_flag = 0;
|
||
if (volume_num == -1){
|
||
if (rv = get_header(find_path + dir_len, hFile, header_buf, buf_size, set_hash,
|
||
&volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
volume_num = -1;
|
||
bad_flag = 1;
|
||
} else {
|
||
j = get_source_num(header_buf, &block_size);
|
||
check_ini_file(set_hash, j); // 前回の検査結果が存在するかどうか
|
||
if (volume_num == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size < (int)data_size)
|
||
rv = buf_size;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf + data_off, (int)data_size);
|
||
} else if (volume_num <= 99){
|
||
parity_num++;
|
||
exist[volume_num - 1] = i;
|
||
if (volume_max < volume_num)
|
||
volume_max = volume_num;
|
||
}
|
||
if (i != volume_num){ // ブロック番号と拡張子が異なるなら
|
||
bad_flag = 3;
|
||
blk = (volume_num > 0) ? 1 : 0;
|
||
}
|
||
}
|
||
} else {
|
||
if (rv = get_header(find_path + dir_len, hFile, header_buf2, buf_size2, hash,
|
||
&volume_num2, &file_num2, &list_off2, NULL, &data_off2, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
bad_flag = 1;
|
||
} else { // 同じ構成の PXX かを確認する
|
||
if (memcmp(hash, set_hash, 16) != 0)
|
||
bad_flag = 2;
|
||
if ((file_num2 != file_num) || (list_off2 != list_off) || (data_off2 != data_off))
|
||
bad_flag = 2;
|
||
if ((block_size) && (volume_num2 > 0)){
|
||
if (data_size != block_size)
|
||
bad_flag = 2;
|
||
}
|
||
if (!bad_flag){
|
||
if (volume_num2 == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size2 < (int)data_size)
|
||
rv = buf_size2;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf2 + data_off2, rv);
|
||
} else if (volume_num2 <= 99){
|
||
if (exist[volume_num2 - 1] == -1) // 係数が重複してるのは使えない
|
||
parity_num++;
|
||
exist[volume_num2 - 1] = i;
|
||
if (volume_max < volume_num2)
|
||
volume_max = volume_num2;
|
||
}
|
||
if (i != volume_num2){ // ブロック番号と拡張子が異なるなら
|
||
bad_flag = 3;
|
||
blk = (volume_num2 > 0) ? 1 : 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
switch (bad_flag){
|
||
case 0:
|
||
printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf);
|
||
break;
|
||
case 1:
|
||
printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf);
|
||
recovery_lost++;
|
||
break;
|
||
case 2:
|
||
printf(" %d / %d Useless : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++;
|
||
break;
|
||
case 3:
|
||
printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない
|
||
break;
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
|
||
fflush(stdout);
|
||
}
|
||
|
||
if (volume_num == -1){ // Recovery Set が見つからなければここで終わる
|
||
printf("valid file is not found\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// セット・ハッシュとプログラムのバージョンを表示する
|
||
printf("\nSet Hash: ");
|
||
print_hash(set_hash);
|
||
memcpy(&rv, header_buf + 12, 4);
|
||
printf("\nCreator : ");
|
||
switch (rv >> 24){
|
||
case 1: // Mirror
|
||
printf("Mirror");
|
||
break;
|
||
case 2: // PAR
|
||
printf("PAR");
|
||
break;
|
||
case 3: // SmartPar
|
||
printf("SmartPar");
|
||
break;
|
||
default:
|
||
printf("Unknown client %d", rv >> 24);
|
||
}
|
||
printf(" version %d.%d.%d\n", (rv >> 16) & 0xFF, (rv >> 8) & 0xFF, rv & 0xFF);
|
||
if ((par_comment[0] != 0) && (!utf16_to_cp(par_comment, ascii_buf)))
|
||
printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する
|
||
|
||
if (((par_flag & 1) == 0) && (switch_p)){ // PAR ファイルを作り直す
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
get_temp_name(find_path, file_name);
|
||
if (recreate_par(file_name, header_buf, header_buf2) == 0){
|
||
if (replace_file(find_path, file_name, switch_b) == 0){ // 破損したPARファイルが存在するならどかす
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
printf("\nRestored file :\n");
|
||
printf(" Size : Filename\n");
|
||
printf("%13u : \"%s\"\n", data_off, ascii_buf);
|
||
} else {
|
||
DeleteFile(file_name);
|
||
}
|
||
}
|
||
}
|
||
free(header_buf2);
|
||
header_buf2 = NULL;
|
||
printf("\nParity Volume count\t: %d\n", volume_max);
|
||
printf("Parity Volume found\t: %d\n", parity_num);
|
||
fflush(stdout);
|
||
|
||
// ファイル・リストを表示する
|
||
printf("\nInput File list : %d\n", file_num);
|
||
printf(" Size B : Filename\n");
|
||
total_file_size = 0;
|
||
source_num = 0;
|
||
list_off2 = list_off;
|
||
for (i = 0; i < file_num; i++){
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off2, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off2 + 8), 2);
|
||
parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set
|
||
if (parity_flag){
|
||
source_num++;
|
||
blk = 1;
|
||
} else {
|
||
blk = 0;
|
||
}
|
||
// size
|
||
memcpy(&data_size, header_buf + (list_off2 + 16), 8);
|
||
total_file_size += data_size;
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off2 + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
// ファイル名が有効かどうか確かめる
|
||
j = sanitize_filename(file_name);
|
||
if (j != 0){
|
||
if (j == 16){
|
||
printf("filename is not valied, %s\n", ascii_buf);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
}
|
||
printf("%13I64u %d : \"%s\"\n", data_size, blk, ascii_buf);
|
||
list_off2 += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
printf("\nData File count : %d\n", source_num);
|
||
printf("Max file size\t: %I64d\n", block_size);
|
||
printf("Total data size : %I64d\n", total_file_size);
|
||
|
||
// ソース・ファイルが存在していて正しいかを調べる
|
||
printf("\nVerifying Input File :\n");
|
||
printf(" Size Status : Filename\n");
|
||
fflush(stdout);
|
||
for (i = 0; i < file_num; i++){
|
||
if (err = cancel_progress()) // キャンセル処理
|
||
goto error_end;
|
||
find_flag = bad_flag = parity_flag = 0;
|
||
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off + 8), 2);
|
||
parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set
|
||
// size
|
||
memcpy(&data_size, header_buf + (list_off + 16), 8);
|
||
file_size = 0;
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
// ファイル名が有効かどうか確かめる
|
||
wcscpy(file_path, file_name);
|
||
j = sanitize_filename(file_path);
|
||
if (j != 0){
|
||
wcscpy(file_name, file_path);
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
}
|
||
// ファイル名を基準ディレクトリに追加してパスにする
|
||
if (base_len + wcslen(file_name) >= MAX_LEN - ADD_LEN){
|
||
printf("filename is too long\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
wcscpy(file_path, base_dir);
|
||
wcscpy(file_path + base_len, file_name);
|
||
|
||
// ファイルが存在するか
|
||
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){ // ファイルが存在するなら内容を確認する
|
||
// 検査結果の記録があるかどうか
|
||
bad_flag = check_ini_state(i, meta_data, hFile);
|
||
memcpy(&file_size, meta_data, 8);
|
||
if (bad_flag == -1){ // ファイル・サイズが不明なら消失扱いにする
|
||
bad_flag = 1;
|
||
} else if (bad_flag == -2){ // 検査結果が無かった場合
|
||
bad_flag = 0;
|
||
if (file_size >= data_size){ // 末尾にゴミが付いてないか調べる
|
||
if (file_size > data_size)
|
||
bad_flag = 2;
|
||
if (data_size > 0){ // 16k MD5 hash が一致するか確かめる
|
||
if (file_md5_16k(hFile, data_size, hash)){
|
||
bad_flag = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){
|
||
bad_flag = 1;
|
||
} else if (data_size > 16384){ // MD5 hash が一致するか確かめる
|
||
if (rv = file_md5(file_name, hFile, data_size, hash)){
|
||
if (rv == 2){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
bad_flag = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){
|
||
bad_flag = 1;
|
||
}
|
||
// 16k MD5 hash が一致した時だけ検査結果を記録する
|
||
write_ini_state(i, meta_data, bad_flag);
|
||
}
|
||
}
|
||
} else if (file_size < data_size){
|
||
bad_flag = 1;
|
||
}
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
if (!bad_flag)
|
||
find_flag = 3;
|
||
}
|
||
|
||
// ファイルが存在しないか、不完全だった場合は、サイズとハッシュ値で検索する
|
||
if ((data_size) && ((hFile == INVALID_HANDLE_VALUE) || (bad_flag))){
|
||
wcscpy(find_path, base_dir);
|
||
wcscpy(find_path + base_len, L"*");
|
||
hFind = FindFirstFile(find_path, &FindData);
|
||
if (hFind != INVALID_HANDLE_VALUE){
|
||
do {
|
||
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ // フォルダは無視する
|
||
find_flag = 1;
|
||
file_size2 = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow);
|
||
if (file_size2 != data_size){ // ファイル・サイズが一致しなければ
|
||
find_flag = 0;
|
||
} else { // ファイル名がリスト上にあれば除外する
|
||
memcpy(&list_off2, header_buf + 64, 4);
|
||
for (j = 0; j < file_num; j++){
|
||
// entry size
|
||
memcpy(&entry_size2, header_buf + list_off2, 4);
|
||
// filename
|
||
len = (entry_size2 - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off2 + 56), len * 2);
|
||
file_name[len] = 0;
|
||
sanitize_filename(file_name);
|
||
if (_wcsicmp(file_name, FindData.cFileName) == 0){
|
||
find_flag = 0;
|
||
break;
|
||
}
|
||
list_off2 += entry_size2; // 次のエントリー位置にずらす
|
||
}
|
||
}
|
||
// リスト上に無くてファイル・サイズが一致すれば
|
||
if ((find_flag != 0) && (base_len + wcslen(FindData.cFileName) < MAX_LEN)){
|
||
wcscpy(find_path + base_len, FindData.cFileName);
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){
|
||
j = check_ini_state(i, meta_data, hFile);
|
||
if (j == -2){ // 検査結果が無かった場合
|
||
if (data_size > 0){ // 16k MD5 hash が一致するか確かめる
|
||
if (file_md5_16k(hFile, data_size, hash)){
|
||
find_flag = 0;
|
||
} else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){
|
||
find_flag = 0;
|
||
} else if (data_size > 16384){ // MD5 hash が一致するか確かめる
|
||
j = 0;
|
||
if (rv = file_md5(FindData.cFileName, hFile, data_size, hash)){
|
||
if (rv == 2){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
find_flag = 0;
|
||
j = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){
|
||
find_flag = 0;
|
||
j = 1;
|
||
}
|
||
// 16k MD5 hash が一致した時だけ検査結果を記録する
|
||
write_ini_state(i, meta_data, j);
|
||
}
|
||
}
|
||
} else if (j != 0){ // 完全以外なら
|
||
find_flag = 0;
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
if (find_flag){ // 異なる名前だが同じ内容のファイルを発見した
|
||
utf16_to_cp(FindData.cFileName, ascii_buf2);
|
||
break;
|
||
}
|
||
} else {
|
||
find_flag = 0;
|
||
}
|
||
}
|
||
}
|
||
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
|
||
FindClose(hFind);
|
||
}
|
||
}
|
||
if (find_flag == 3){
|
||
printf(" = Complete : \"%s\"\n", ascii_buf);
|
||
} else if (find_flag == 1){
|
||
need_repair++;
|
||
if (bad_flag){
|
||
printf("%13I64u Damaged~ : \"%s\"\n", file_size, ascii_buf);
|
||
} else {
|
||
printf(" - Missing~ : \"%s\"\n", ascii_buf);
|
||
}
|
||
printf("%13I64u Misnamed : \"%s\"\n", data_size, ascii_buf2);
|
||
} else if (bad_flag == 2){
|
||
need_repair++;
|
||
printf("%13I64u Appended : \"%s\"\n", file_size, ascii_buf);
|
||
} else if (bad_flag){
|
||
if (parity_flag)
|
||
block_lost++;
|
||
if (data_size == 0)
|
||
need_repair++;
|
||
printf("%13I64u Damaged : \"%s\"\n", file_size, ascii_buf);
|
||
} else {
|
||
if (parity_flag)
|
||
block_lost++;
|
||
if (data_size == 0)
|
||
need_repair++;
|
||
printf(" - Missing : \"%s\"\n", ascii_buf);
|
||
}
|
||
|
||
fflush(stdout);
|
||
list_off += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
printf("\nData File lost\t: %d\n\n", block_lost);
|
||
if ((block_lost == 0) && (need_repair == 0))
|
||
printf("All Files Complete\n");
|
||
if (recovery_lost > 0){ // 不完全なリカバリ・ファイルがあるなら
|
||
err = 256;
|
||
printf("%d PAR File(s) Incomplete\n", recovery_lost);
|
||
}
|
||
|
||
// 修復する必要があるかどうか
|
||
if ((block_lost > 0) || (need_repair > 0)){
|
||
err |= 4;
|
||
if (need_repair > 0){
|
||
printf("Ready to rename %d file(s)\n", need_repair);
|
||
err |= 32;
|
||
}
|
||
if (block_lost > 0){
|
||
if (block_lost > parity_num){
|
||
printf("Need %d more volume(s) to repair %d file(s)\n", block_lost - parity_num, block_lost);
|
||
err |= 8;
|
||
} else {
|
||
printf("Ready to repair %d file(s)\n", block_lost);
|
||
err |= 128;
|
||
}
|
||
}
|
||
}
|
||
|
||
error_end:
|
||
close_ini_file();
|
||
if (hFile != NULL)
|
||
CloseHandle(hFile);
|
||
if (header_buf)
|
||
free(header_buf);
|
||
if (header_buf2)
|
||
free(header_buf2);
|
||
return err;
|
||
}
|
||
|
||
// ソース・ファイルの破損や欠損を修復する
|
||
int par1_repair(
|
||
int switch_b, // 既存のファイルを別名にしてどかす
|
||
int switch_p, // インデックス・ファイルを作り直す
|
||
wchar_t *par_comment) // コメント
|
||
{
|
||
char ascii_buf[MAX_LEN * 3], ascii_buf2[MAX_LEN * 3];
|
||
unsigned char *header_buf = NULL, *header_buf2 = NULL, hash[16], set_hash[16];
|
||
wchar_t file_name[MAX_LEN * 2], file_path[MAX_LEN], find_path[MAX_LEN * 2];
|
||
wchar_t par_char[] = L"AR";
|
||
int err = 0, i, j, bad_flag, find_flag, parity_flag, par_flag, exist[256];
|
||
int len, dir_len, base_len, buf_size, entry_size, entry_size2, blk;
|
||
int volume_num, volume_max, file_num, list_off, list_size, data_off;
|
||
int buf_size2, volume_num2, file_num2, list_off2, data_off2;
|
||
int parity_num, source_num, block_lost = 0, need_repair = 0, recovery_lost = 0;
|
||
int parity_max, block_max;
|
||
unsigned int rv, meta_data[7];
|
||
__int64 file_size, file_size2, data_size, block_size = 0, total_file_size;
|
||
HANDLE hFile = NULL, hFind;
|
||
WIN32_FIND_DATA FindData;
|
||
file_ctx *files = NULL, *tmp_p;
|
||
|
||
// リカバリ・ファイルの名前
|
||
wcscpy(file_path, recovery_file);
|
||
len = wcslen(file_path);
|
||
file_path[len - 2] = 0; // 末尾の2文字を消去する
|
||
if (file_path[len - 3] == 'p'){
|
||
par_char[0] = 'a';
|
||
par_char[1] = 'r';
|
||
}
|
||
// ディレクトリの長さ
|
||
dir_len = len - 3;
|
||
while (dir_len >= 0){
|
||
if ((file_path[dir_len] == '\\') || (file_path[dir_len] == '/'))
|
||
break;
|
||
dir_len--;
|
||
}
|
||
dir_len++;
|
||
base_len = wcslen(base_dir);
|
||
|
||
// リカバリ・ファイルの一覧を表示する
|
||
printf("PAR File list :\n");
|
||
printf(" Size : Filename\n");
|
||
total_file_size = 0;
|
||
parity_num = 0;
|
||
volume_max = 0;
|
||
for (i = 0; i <= 99; i++){
|
||
if (i == 0){
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
blk = 0;
|
||
} else {
|
||
swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i);
|
||
blk = 1;
|
||
}
|
||
// ファイルを開くことができるか、サイズを取得できるかを確かめる
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
continue;
|
||
}
|
||
// ファイルのサイズを取得する
|
||
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
|
||
CloseHandle(hFile);
|
||
continue;
|
||
}
|
||
CloseHandle(hFile);
|
||
total_file_size += file_size;
|
||
parity_num++;
|
||
volume_max = i;
|
||
// リカバリ・ファイルの名前
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
printf("%13I64d : \"%s\"\n", file_size, ascii_buf);
|
||
}
|
||
hFile = NULL;
|
||
printf("\nPAR File total size\t: %I64d\n", total_file_size);
|
||
printf("PAR File possible count\t: %d\n\n", parity_num);
|
||
|
||
// 指定されたリカバリ・ファイルを開く
|
||
hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf("valid file is not found\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// ファイルのサイズを取得する
|
||
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// ヘッダー用のバッファーを確保する
|
||
buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ
|
||
if ((__int64)buf_size > file_size)
|
||
buf_size = (unsigned int)file_size;
|
||
header_buf = malloc(buf_size);
|
||
if (header_buf == NULL){
|
||
printf("malloc, %d\n", buf_size);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// PAR や PXX ファイルとして正しいかを調べる
|
||
parity_max = 0;
|
||
par_flag = 0;
|
||
par_comment[0] = 0;
|
||
for (i = 0; i < 256; i++)
|
||
exist[i] = -1;
|
||
if (rv = get_header(NULL, hFile, header_buf, buf_size, set_hash,
|
||
&volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
volume_num = -1;
|
||
par_flag |= 2;
|
||
} else {
|
||
// 各ブロックが存在するかを記録していく
|
||
block_max = get_source_num(header_buf, &block_size);
|
||
check_ini_file(set_hash, block_max); // 前回の検査結果が存在するかどうか
|
||
if (volume_num == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size < (int)data_size)
|
||
rv = buf_size;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf + data_off, rv);
|
||
}
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
|
||
// 他のリカバリ・ファイルのヘッダー用のバッファーを確保する
|
||
if (volume_num == -1){
|
||
buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ
|
||
} else {
|
||
buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * file_num);
|
||
}
|
||
header_buf2 = malloc(buf_size2);
|
||
if (header_buf2 == NULL){
|
||
printf("malloc, %d\n", buf_size2);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// 他のリカバリ・ファイルも調べる
|
||
printf("Loading PAR File:\n");
|
||
printf(" Block Status : Filename\n");
|
||
fflush(stdout);
|
||
parity_num = 0;
|
||
for (i = 0; i <= 99; i++){
|
||
if (err = cancel_progress()) // キャンセル処理
|
||
goto error_end;
|
||
if (i == 0){
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
blk = 0;
|
||
} else {
|
||
swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i);
|
||
blk = 1;
|
||
}
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
|
||
// 既にチェック済みなら
|
||
if ((_wcsicmp(recovery_file + dir_len, find_path + dir_len) == 0) && (volume_num != -1)){
|
||
if ((volume_num > 0) && (volume_num <= 99)){
|
||
if (exist[block_max + volume_num - 1] == -1) // 係数が重複してるのは使えない
|
||
parity_num++;
|
||
if (parity_max < volume_num)
|
||
parity_max = volume_num;
|
||
if (block_max + volume_num <= 256)
|
||
exist[block_max + volume_num - 1] = i;
|
||
if (volume_max < volume_num)
|
||
volume_max = volume_num;
|
||
}
|
||
if ((par_flag & 2) != 0){
|
||
printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf);
|
||
recovery_lost++;
|
||
} else if (i == volume_num){
|
||
printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf);
|
||
} else {
|
||
blk = (volume_num > 0) ? 1 : 0;
|
||
printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// ファイルを開く
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE)
|
||
continue;
|
||
// PAR や PXX ファイルとして正しいかを調べる
|
||
bad_flag = 0;
|
||
if (volume_num == -1){
|
||
if (rv = get_header(find_path + dir_len, hFile, header_buf, buf_size, set_hash,
|
||
&volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
volume_num = -1;
|
||
bad_flag = 1;
|
||
} else {
|
||
block_max = get_source_num(header_buf, &block_size);
|
||
check_ini_file(set_hash, block_max); // 前回の検査結果が存在するかどうか
|
||
if (volume_num == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size < (int)data_size)
|
||
rv = buf_size;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf + data_off, (int)data_size);
|
||
} else if (volume_num <= 99){
|
||
parity_num++;
|
||
if (parity_max < volume_num)
|
||
parity_max = volume_num;
|
||
if (block_max + volume_num <= 256)
|
||
exist[block_max + volume_num - 1] = i;
|
||
if (volume_max < volume_num)
|
||
volume_max = volume_num;
|
||
}
|
||
if (i != volume_num){ // ブロック番号と拡張子が異なるなら
|
||
bad_flag = 3;
|
||
blk = (volume_num > 0) ? 1 : 0;
|
||
}
|
||
}
|
||
} else {
|
||
if (rv = get_header(find_path + dir_len, hFile, header_buf2, buf_size2, hash,
|
||
&volume_num2, &file_num2, &list_off2, NULL, &data_off2, &data_size)){
|
||
if (rv == 2){ // キャンセル
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
bad_flag = 1;
|
||
} else { // 同じ構成の PXX かを確認する
|
||
if (memcmp(hash, set_hash, 16) != 0)
|
||
bad_flag = 2;
|
||
if ((file_num2 != file_num) || (list_off2 != list_off) || (data_off2 != data_off))
|
||
bad_flag = 2;
|
||
if ((block_size) && (volume_num2 > 0)){
|
||
if (data_size != block_size)
|
||
bad_flag = 2;
|
||
}
|
||
if (!bad_flag){
|
||
if (volume_num2 == 0){
|
||
par_flag |= 1;
|
||
rv = (int)data_size;
|
||
if (buf_size2 < (int)data_size)
|
||
rv = buf_size2;
|
||
if (rv > 1)
|
||
read_comment(par_comment, header_buf2 + data_off2, rv);
|
||
} else if (volume_num2 <= 99){
|
||
if (exist[block_max + volume_num2 - 1] == -1) // 係数が重複してるのは使えない
|
||
parity_num++;
|
||
// 各ブロックが存在するかを記録していく
|
||
if (parity_max < volume_num2)
|
||
parity_max = volume_num2;
|
||
if (block_max + volume_num2 <= 256)
|
||
exist[block_max + volume_num2 - 1] = i;
|
||
if (volume_max < volume_num2)
|
||
volume_max = volume_num2;
|
||
}
|
||
if (i != volume_num2){ // ブロック番号と拡張子が異なるなら
|
||
bad_flag = 3;
|
||
blk = (volume_num2 > 0) ? 1 : 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
switch (bad_flag){
|
||
case 0:
|
||
printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf);
|
||
break;
|
||
case 1:
|
||
printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf);
|
||
recovery_lost++;
|
||
break;
|
||
case 2:
|
||
printf(" %d / %d Useless : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++;
|
||
break;
|
||
case 3:
|
||
printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf);
|
||
recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない
|
||
break;
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
|
||
fflush(stdout);
|
||
}
|
||
|
||
if (volume_num == -1){ // Recovery Set が見つからなければここで終わる
|
||
printf("valid file is not found\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
// セット・ハッシュとプログラムのバージョンを表示する
|
||
printf("\nSet Hash: ");
|
||
print_hash(set_hash);
|
||
memcpy(&rv, header_buf + 12, 4);
|
||
printf("\nCreator : ");
|
||
switch (rv >> 24){
|
||
case 1: // Mirror
|
||
printf("Mirror");
|
||
break;
|
||
case 2: // PAR
|
||
printf("PAR");
|
||
break;
|
||
case 3: // SmartPar
|
||
printf("SmartPar");
|
||
break;
|
||
default:
|
||
printf("Unknown client %d", rv >> 24);
|
||
}
|
||
printf(" version %d.%d.%d\n", (rv >> 16) & 0xFF, (rv >> 8) & 0xFF, rv & 0xFF);
|
||
if ((par_comment[0] != 0) && (!utf16_to_cp(par_comment, ascii_buf)))
|
||
printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する
|
||
|
||
if (((par_flag & 1) == 0) && (switch_p)){ // PAR ファイルを作り直す
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
get_temp_name(find_path, file_name);
|
||
if (recreate_par(file_name, header_buf, header_buf2) == 0){
|
||
if (replace_file(find_path, file_name, switch_b) == 0){ // 破損したPARファイルが存在するならどかす
|
||
utf16_to_cp(find_path + dir_len, ascii_buf);
|
||
printf("\nRestored file :\n");
|
||
printf(" Size : Filename\n");
|
||
printf("%13u : \"%s\"\n", data_off, ascii_buf);
|
||
} else {
|
||
DeleteFile(file_name);
|
||
}
|
||
}
|
||
}
|
||
free(header_buf2);
|
||
header_buf2 = NULL;
|
||
printf("\nParity Volume count\t: %d\n", volume_max);
|
||
printf("Parity Volume found\t: %d\n", parity_num);
|
||
fflush(stdout);
|
||
|
||
// リカバリ・ファイルを開く
|
||
len = sizeof(file_ctx) * (block_max + parity_max);
|
||
files = malloc(len);
|
||
if (files == NULL){
|
||
printf("malloc, %d\n", len);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
for (i = 0; i < (block_max + parity_max); i++)
|
||
files[i].hFile = NULL;
|
||
for (i = block_max; i < (block_max + parity_max); i++){
|
||
if (exist[i] != -1){ // リカバリ・ファイルが存在するなら
|
||
if (exist[i] == 0){
|
||
swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char);
|
||
} else {
|
||
swprintf(find_path, MAX_LEN, L"%s%02d", file_path, exist[i]);
|
||
}
|
||
files[i].hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (files[i].hFile == INVALID_HANDLE_VALUE){
|
||
exist[i] = 0;
|
||
continue;
|
||
}
|
||
// ファイル・サイズを取得する
|
||
if (!GetFileSizeEx(files[i].hFile, (PLARGE_INTEGER)&file_size2)){
|
||
exist[i] = 0;
|
||
continue;
|
||
}
|
||
files[i].size = file_size2;
|
||
files[i].size -= data_off;
|
||
//printf("%d: off = %d, size = %I64u \n", i, data_off, files[i].size);
|
||
// 開始位置をリード・ソロモン符号のデータの所にしておく
|
||
if (SetFilePointer(files[i].hFile, data_off, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){
|
||
exist[i] = 0;
|
||
continue;
|
||
}
|
||
exist[i] = 1;
|
||
} else {
|
||
exist[i] = 0;
|
||
}
|
||
}
|
||
for (i = 0; i < block_max; i++)
|
||
exist[i] = 0;
|
||
|
||
// ファイル・リストを表示する
|
||
printf("\nInput File list : %d\n", file_num);
|
||
printf(" Size B : Filename\n");
|
||
total_file_size = 0;
|
||
source_num = 0;
|
||
list_off2 = list_off;
|
||
for (i = 0; i < file_num; i++){
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off2, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off2 + 8), 2);
|
||
parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set
|
||
if (parity_flag){
|
||
source_num++;
|
||
blk = 1;
|
||
} else {
|
||
blk = 0;
|
||
}
|
||
// size
|
||
memcpy(&data_size, header_buf + (list_off2 + 16), 8);
|
||
total_file_size += data_size;
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off2 + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
// ファイル名が有効かどうか確かめる
|
||
j = sanitize_filename(file_name);
|
||
if (j != 0){
|
||
if (j == 16){
|
||
printf("filename is not valied, %s\n", ascii_buf);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
}
|
||
printf("%13I64u %d : \"%s\"\n", data_size, blk, ascii_buf);
|
||
list_off2 += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
printf("\nData File count : %d\n", source_num);
|
||
printf("Max file size\t: %I64d\n", block_size);
|
||
printf("Total data size : %I64d\n", total_file_size);
|
||
|
||
// ソース・ファイルが存在していて正しいかを調べる
|
||
printf("\nVerifying Input File :\n");
|
||
printf(" Size Status : Filename\n");
|
||
fflush(stdout);
|
||
source_num = 0;
|
||
for (i = 0; i < file_num; i++){
|
||
if (err = cancel_progress()) // キャンセル処理
|
||
goto error_end;
|
||
find_flag = bad_flag = parity_flag = 0;
|
||
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off + 8), 2);
|
||
parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set
|
||
if (parity_flag)
|
||
source_num++;
|
||
// size
|
||
memcpy(&data_size, header_buf + (list_off + 16), 8);
|
||
file_size = 0;
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
// ファイル名が有効かどうか確かめる
|
||
wcscpy(file_path, file_name);
|
||
j = sanitize_filename(file_path);
|
||
if (j != 0){
|
||
wcscpy(file_name, file_path);
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
}
|
||
// ファイル名を基準ディレクトリに追加してパスにする
|
||
if (base_len + wcslen(file_name) >= MAX_LEN - ADD_LEN){
|
||
printf("filename is too long\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
wcscpy(file_path, base_dir);
|
||
wcscpy(file_path + base_len, file_name);
|
||
|
||
// ファイルが存在するか
|
||
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){ // ファイルが存在するなら内容を確認する
|
||
// 検査結果の記録があるかどうか
|
||
bad_flag = check_ini_state(i, meta_data, hFile);
|
||
memcpy(&file_size, meta_data, 8);
|
||
if (bad_flag == -1){ // ファイル・サイズが不明なら消失扱いにする
|
||
bad_flag = 1;
|
||
} else if (bad_flag == -2){ // 検査結果が無かった場合
|
||
bad_flag = 0;
|
||
if (file_size >= data_size){ // 末尾にゴミが付いてないか調べる
|
||
if (file_size > data_size)
|
||
bad_flag = 2;
|
||
if (data_size > 0){ // 16k MD5 hash が一致するか確かめる
|
||
if (file_md5_16k(hFile, data_size, hash)){
|
||
bad_flag = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){
|
||
bad_flag = 1;
|
||
} else if (data_size > 16384){ // MD5 hash が一致するか確かめる
|
||
if (rv = file_md5(file_name, hFile, data_size, hash)){
|
||
if (rv == 2){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
bad_flag = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){
|
||
bad_flag = 1;
|
||
}
|
||
// 16k MD5 hash が一致した時だけ検査結果を記録する
|
||
write_ini_state(i, meta_data, bad_flag);
|
||
}
|
||
}
|
||
} else if (file_size < data_size){
|
||
bad_flag = 1;
|
||
}
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
if (!bad_flag){
|
||
find_flag = 3;
|
||
if (parity_flag)
|
||
exist[source_num - 1] = 1;
|
||
}
|
||
}
|
||
|
||
// ファイルが存在しないか、不完全だった場合は、サイズとハッシュ値で検索する
|
||
if ((data_size) && ((hFile == INVALID_HANDLE_VALUE) || (bad_flag))){
|
||
wcscpy(find_path, base_dir);
|
||
wcscpy(find_path + base_len, L"*");
|
||
hFind = FindFirstFile(find_path, &FindData);
|
||
if (hFind != INVALID_HANDLE_VALUE){
|
||
do {
|
||
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ // フォルダは無視する
|
||
find_flag = 1;
|
||
file_size2 = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow);
|
||
if (file_size2 != data_size){ // ファイル・サイズが一致しなければ
|
||
find_flag = 0;
|
||
} else { // ファイル名がリスト上にあれば除外する
|
||
memcpy(&list_off2, header_buf + 64, 4);
|
||
for (j = 0; j < file_num; j++){
|
||
// entry size
|
||
memcpy(&entry_size2, header_buf + list_off2, 4);
|
||
// filename
|
||
len = (entry_size2 - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off2 + 56), len * 2);
|
||
file_name[len] = 0;
|
||
sanitize_filename(file_name);
|
||
if (_wcsicmp(file_name, FindData.cFileName) == 0){
|
||
find_flag = 0;
|
||
break;
|
||
}
|
||
list_off2 += entry_size2; // 次のエントリー位置にずらす
|
||
}
|
||
}
|
||
// リスト上に無くてファイル・サイズが一致すれば
|
||
if ((find_flag != 0) && (base_len + wcslen(FindData.cFileName) < MAX_LEN)){
|
||
wcscpy(find_path + base_len, FindData.cFileName);
|
||
hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){
|
||
j = check_ini_state(i, meta_data, hFile);
|
||
if (j == -2){ // 検査結果が無かった場合
|
||
if (data_size > 0){ // 16k MD5 hash が一致するか確かめる
|
||
if (file_md5_16k(hFile, data_size, hash)){
|
||
find_flag = 0;
|
||
} else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){
|
||
find_flag = 0;
|
||
} else if (data_size > 16384){ // MD5 hash が一致するか確かめる
|
||
j = 0;
|
||
if (rv = file_md5(FindData.cFileName, hFile, data_size, hash)){
|
||
if (rv == 2){
|
||
err = 2;
|
||
goto error_end;
|
||
}
|
||
find_flag = 0;
|
||
j = 1;
|
||
} else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){
|
||
find_flag = 0;
|
||
j = 1;
|
||
}
|
||
// 16k MD5 hash が一致した時だけ検査結果を記録する
|
||
write_ini_state(i, meta_data, j);
|
||
}
|
||
}
|
||
} else if (j != 0){ // 完全以外なら
|
||
find_flag = 0;
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
if (find_flag){ // 異なる名前だが同じ内容のファイルを発見した
|
||
utf16_to_cp(FindData.cFileName, ascii_buf2);
|
||
// ファイル名を修正する
|
||
if (replace_file(file_path, find_path, switch_b) == 0){
|
||
find_flag = 2;
|
||
// } else {
|
||
// printf("cannot rename to %s\n", ascii_buf);
|
||
// err = 1;
|
||
// goto error_end;
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
find_flag = 0;
|
||
}
|
||
}
|
||
}
|
||
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
|
||
FindClose(hFind);
|
||
}
|
||
}
|
||
if (find_flag == 3){
|
||
printf(" = Complete : \"%s\"\n", ascii_buf);
|
||
} else if (find_flag == 1){
|
||
need_repair++;
|
||
need_repair |= 0x40000000; // 修復失敗の印
|
||
if (bad_flag){
|
||
printf("%13I64u Failed~ : \"%s\"\n", file_size, ascii_buf);
|
||
} else {
|
||
printf(" - Failed : \"%s\"\n", ascii_buf);
|
||
}
|
||
printf("%13I64u Misnamed : \"%s\"\n", data_size, ascii_buf2);
|
||
} else if (find_flag == 2){
|
||
need_repair += 0x10001; // 修復できた数を記録する
|
||
if (parity_flag)
|
||
exist[source_num - 1] = 1;
|
||
printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf);
|
||
} else if (bad_flag == 2){
|
||
if (shorten_file(file_path, data_size, switch_b) != 0) // ファイルを小さくする
|
||
bad_flag = 1;
|
||
need_repair++;
|
||
if (bad_flag == 2){ // 修復 (縮小) できたら
|
||
need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する
|
||
if (parity_flag)
|
||
exist[source_num - 1] = 1;
|
||
printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf);
|
||
} else { // 修復できなかったなら
|
||
if (parity_flag)
|
||
block_lost++;
|
||
printf("%13I64u Appended : \"%s\"\n", file_size, ascii_buf);
|
||
}
|
||
} else if (bad_flag){
|
||
bad_flag = 1;
|
||
if (data_size == 0){ // サイズが 0ならすぐに復元できる
|
||
need_repair++;
|
||
hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){
|
||
bad_flag = 0;
|
||
need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
}
|
||
if (bad_flag){
|
||
if (parity_flag)
|
||
block_lost++;
|
||
printf("%13I64u Damaged : \"%s\"\n", file_size, ascii_buf);
|
||
} else {
|
||
if (parity_flag)
|
||
exist[source_num - 1] = 1;
|
||
printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf);
|
||
}
|
||
} else {
|
||
bad_flag = 1;
|
||
if (data_size == 0){ // サイズが 0ならすぐに復元できる
|
||
need_repair++;
|
||
hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFile != INVALID_HANDLE_VALUE){
|
||
bad_flag = 0;
|
||
need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
}
|
||
if (bad_flag){
|
||
if (parity_flag)
|
||
block_lost++;
|
||
printf(" - Missing : \"%s\"\n", ascii_buf);
|
||
} else {
|
||
if (parity_flag)
|
||
exist[source_num - 1] = 1;
|
||
printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf);
|
||
}
|
||
}
|
||
|
||
if (parity_flag){
|
||
// ソース・ファイルを開く
|
||
if (exist[source_num - 1]){ // ソース・ブロックが存在するなら
|
||
files[source_num - 1].hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (files[source_num - 1].hFile == INVALID_HANDLE_VALUE){
|
||
exist[source_num - 1] = 0;
|
||
} else {
|
||
if (!GetFileSizeEx(files[source_num - 1].hFile, (PLARGE_INTEGER)&file_size2)){
|
||
exist[source_num - 1] = 0;
|
||
} else {
|
||
files[source_num - 1].size = file_size2;
|
||
}
|
||
}
|
||
} else { // 破損してる、または存在しないなら
|
||
get_temp_name(file_path, find_path); // 修復中のテンポラリ・ファイル
|
||
files[source_num - 1].hFile = CreateFile(find_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (files[source_num - 1].hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf_cp("cannot create file, %s\n", find_path);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
files[source_num - 1].size = data_size;
|
||
}
|
||
//printf("%d: exist = %d, size = %I64u \n", source_num - 1, exist[source_num - 1], files[source_num - 1].size);
|
||
}
|
||
|
||
fflush(stdout);
|
||
list_off += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
printf("\nData File lost\t: %d\n\n", block_lost);
|
||
|
||
// 修復に必要なパリティ・ブロックの数が少なくて済むなら最大値を調節する
|
||
if (block_lost < parity_num){
|
||
volume_num = 0;
|
||
len = block_max + parity_max;
|
||
for (i = block_max; i < len; i++){
|
||
if (exist[i]){
|
||
volume_num++;
|
||
if (volume_num > block_lost){
|
||
volume_num--;
|
||
exist[i] = 0;
|
||
CloseHandle(files[i].hFile);
|
||
files[i].hFile = NULL;
|
||
} else {
|
||
parity_max = 1 + i - block_max;
|
||
}
|
||
}
|
||
}
|
||
len = sizeof(file_ctx) * (block_max + parity_max);
|
||
tmp_p = (file_ctx *)realloc(files, len);
|
||
if (tmp_p == NULL){
|
||
printf("realloc, %d\n", len);
|
||
err = 1;
|
||
goto error_end;
|
||
} else {
|
||
files = tmp_p;
|
||
}
|
||
}
|
||
|
||
if ((block_lost == 0) && (need_repair == 0))
|
||
printf("All Files Complete\n");
|
||
if (recovery_lost > 0){ // 不完全なリカバリ・ファイルがあるなら
|
||
err = 256;
|
||
printf("%d PAR File(s) Incomplete\n", recovery_lost);
|
||
}
|
||
|
||
// 修復する必要があるかどうか
|
||
if (block_lost == 0){
|
||
if (need_repair > 0){
|
||
need_repair &= 0x3FFFFFFF;
|
||
printf("Restored file count\t: %d\n\n", need_repair >> 16);
|
||
need_repair = (need_repair & 0xFFFF) - (need_repair >> 16);
|
||
if (need_repair == 0){ // 全て修復できたのなら
|
||
printf("Repaired successfully\n");
|
||
err |= 16;
|
||
} else {
|
||
printf("Failed to repair %d file(s)\n", need_repair);
|
||
err |= 16 | 4;
|
||
}
|
||
}
|
||
goto error_end;
|
||
} else {
|
||
if (need_repair > 0){
|
||
printf("Restored file count\t: %d\n\n", (need_repair >> 16) & 0x3FFF);
|
||
if (need_repair & 0x40000000){
|
||
need_repair = (need_repair & 0xFFFF) - ((need_repair >> 16) & 0x3FFF);
|
||
printf("Failed to repair %d file(s)\n", block_lost + need_repair);
|
||
err |= 16 | 4;
|
||
goto error_end;
|
||
}
|
||
need_repair &= 0x3FFFFFFF;
|
||
need_repair = (need_repair & 0xFFFF) - (need_repair >> 16);
|
||
}
|
||
if (block_lost > parity_num){
|
||
printf("Need %d more volume(s) to repair %d file(s)\n", block_lost - parity_num, block_lost);
|
||
err |= 8 | 4;
|
||
goto error_end;
|
||
} else {
|
||
printf("Ready to repair %d file(s)\n", block_lost);
|
||
}
|
||
}
|
||
|
||
// 失われたブロックを復元する
|
||
printf("\n");
|
||
print_progress_text(0, "Recovering data");
|
||
if (err = rs_decode(block_max, block_lost, block_size, parity_max, exist, files)){
|
||
goto error_end;
|
||
}
|
||
|
||
// 正しく修復できたか確かめる
|
||
printf("\nVerifying repair: %d\n", block_lost);
|
||
printf(" Status : Filename\n");
|
||
fflush(stdout);
|
||
wcscpy(file_path, base_dir);
|
||
find_flag = 0;
|
||
source_num = 0;
|
||
memcpy(&list_off, header_buf + 64, 4);
|
||
for (i = 0; i < file_num; i++){
|
||
bad_flag = 0;
|
||
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off + 8), 2);
|
||
parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set
|
||
// size
|
||
memcpy(&data_size, header_buf + (list_off + 16), 8);
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
sanitize_filename(file_name);
|
||
|
||
// ファイル名を基準ディレクトリに追加してパスにする
|
||
wcscpy(file_path + base_len, file_name);
|
||
|
||
//printf("%d : list_off = %d, exist = %d \n", i, list_off, exist[source_num]);
|
||
// 修復したファイルのハッシュ値を計算する
|
||
if (parity_flag){
|
||
if (exist[source_num] == 0){
|
||
if (file_md5(file_name, files[source_num].hFile, data_size, hash)){
|
||
bad_flag = 1;
|
||
} else {
|
||
for (j = 0; j < 16; j++){
|
||
if (hash[j] != header_buf[list_off + 24 + j]){
|
||
bad_flag = 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
CloseHandle(files[source_num].hFile);
|
||
files[source_num].hFile = NULL;
|
||
get_temp_name(file_path, find_path);
|
||
if (bad_flag){ // 失敗
|
||
DeleteFile(find_path); // テンポラリ・ファイルを削除する
|
||
printf(" Failed : \"%s\"\n", ascii_buf);
|
||
} else { // 復元成功
|
||
if (replace_file(file_path, find_path, switch_b) == 0){ // 修復したファイルを戻す
|
||
block_lost--;
|
||
find_flag++;
|
||
printf(" Repaired : \"%s\"\n", ascii_buf);
|
||
} else { // ファイルを戻せなかった場合は、テンポラリ・ファイルを残した方がいいかも?
|
||
// まあ PAR2 と違ってすぐに修復できるから、削除してもよさそう。
|
||
DeleteFile(find_path); // テンポラリ・ファイルを削除する
|
||
printf(" Locked : \"%s\"\n", ascii_buf);
|
||
}
|
||
}
|
||
fflush(stdout);
|
||
}
|
||
source_num++;
|
||
}
|
||
|
||
list_off += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
printf("\nRepaired file count\t: %d\n\n", find_flag);
|
||
|
||
err = 16;
|
||
if (block_lost + need_repair == 0){ // 全て修復できたなら
|
||
printf("Repaired successfully\n");
|
||
} else {
|
||
printf("Failed to repair %d file(s)\n", block_lost + need_repair);
|
||
err |= 4;
|
||
}
|
||
if (recovery_lost > 0)
|
||
err |= 256;
|
||
|
||
error_end:
|
||
close_ini_file();
|
||
if (files){
|
||
// エラーが発生したら、作業中のファイルを削除する
|
||
source_num = 0;
|
||
memcpy(&list_off, header_buf + 64, 4);
|
||
for (i = 0; i < file_num; i++){
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off + 8), 2);
|
||
parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off + 56), len * 2);
|
||
file_name[len] = 0;
|
||
sanitize_filename(file_name);
|
||
|
||
// ファイル名を基準ディレクトリに追加してパスにする
|
||
wcscpy(file_path + base_len, file_name);
|
||
|
||
if (parity_flag){
|
||
if ((exist[source_num] == 0) && (files[source_num].hFile)){
|
||
CloseHandle(files[source_num].hFile);
|
||
files[source_num].hFile = NULL;
|
||
get_temp_name(file_path, find_path);
|
||
DeleteFile(find_path); // テンポラリ・ファイルを削除する
|
||
}
|
||
source_num++;
|
||
}
|
||
|
||
list_off += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
for (i = 0; i < (source_num + parity_max); i++){
|
||
if (files[i].hFile != NULL)
|
||
CloseHandle(files[i].hFile);
|
||
}
|
||
free(files);
|
||
}
|
||
if (hFile != NULL)
|
||
CloseHandle(hFile);
|
||
if (header_buf)
|
||
free(header_buf);
|
||
if (header_buf2)
|
||
free(header_buf2);
|
||
return err;
|
||
}
|
||
|
||
// ソース・ファイルの一覧を表示する
|
||
int par1_list(
|
||
int switch_h, // ハッシュ値も表示する
|
||
wchar_t *par_comment) // コメント
|
||
{
|
||
char ascii_buf[MAX_LEN * 3];
|
||
unsigned char *header_buf = NULL, hash[16];
|
||
wchar_t file_name[MAX_LEN];
|
||
int err = 0, i, parity_flag;
|
||
int len, buf_size, entry_size, blk;
|
||
int volume_num, file_num, list_off, list_size, data_off;
|
||
int source_num = 0;
|
||
__int64 file_size, data_size, block_size = 0;
|
||
HANDLE hFile = NULL;
|
||
|
||
// 指定されたリカバリ・ファイルを開く
|
||
hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
printf("cannot open PAR file\n");
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// ファイルのサイズを取得する
|
||
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
|
||
print_win32_err();
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// ヘッダー用のバッファーを確保する
|
||
buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ
|
||
if ((__int64)buf_size > file_size)
|
||
buf_size = (unsigned int)file_size;
|
||
header_buf = malloc(buf_size);
|
||
if (header_buf == NULL){
|
||
printf("malloc, %d\n", buf_size);
|
||
err = 1;
|
||
goto error_end;
|
||
}
|
||
|
||
// PAR や PXX ファイルとして正しいかを調べる
|
||
if (err = get_header(NULL, hFile, header_buf, buf_size, hash,
|
||
&volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){
|
||
if (err != 2) // キャンセル以外なら
|
||
printf("valid file is not found\n");
|
||
goto error_end;
|
||
} else {
|
||
if ((volume_num == 0) && (data_size > 0)){ // コメントがある
|
||
len = (int)data_size;
|
||
if (buf_size < (int)data_size)
|
||
len = buf_size;
|
||
if (len > 1)
|
||
read_comment(par_comment, header_buf + data_off, len);
|
||
if (!utf16_to_cp(par_comment, ascii_buf))
|
||
printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する
|
||
}
|
||
}
|
||
CloseHandle(hFile);
|
||
hFile = NULL;
|
||
|
||
printf("Input File list : %d\n", file_num);
|
||
if (switch_h){
|
||
printf(" Size B MD5 Hash : Filename\n");
|
||
} else {
|
||
printf(" Size B : Filename\n");
|
||
}
|
||
// ソース・ファイルの情報を表示する
|
||
for (i = 0; i < file_num; i++){
|
||
// entry size
|
||
memcpy(&entry_size, header_buf + list_off, 4);
|
||
// status field
|
||
memcpy(&parity_flag, header_buf + (list_off + 8), 2);
|
||
parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set
|
||
if (parity_flag){
|
||
source_num++;
|
||
blk = 1;
|
||
} else {
|
||
blk = 0;
|
||
}
|
||
// size
|
||
memcpy(&file_size, header_buf + (list_off + 16), 8);
|
||
if (file_size > block_size)
|
||
block_size = file_size;
|
||
// hash
|
||
memcpy(hash, header_buf + (list_off + 40), 16);
|
||
// filename
|
||
len = (entry_size - 56) / 2;
|
||
if (len >= MAX_LEN)
|
||
len = MAX_LEN - 1;
|
||
memcpy(file_name, header_buf + (list_off + 56), len * 2);
|
||
file_name[len] = 0;
|
||
utf16_to_cp(file_name, ascii_buf);
|
||
|
||
// 項目番号、ソース・ブロックかどうか、ファイル・サイズ、MD5ハッシュ値、ファイル名
|
||
if (switch_h){
|
||
printf("%13I64u %d ", file_size, blk);
|
||
print_hash(hash);
|
||
printf(" : \"%s\"\n", ascii_buf);
|
||
} else {
|
||
printf("%13I64u %d : \"%s\"\n", file_size, blk, ascii_buf);
|
||
}
|
||
|
||
list_off += entry_size; // 次のエントリー位置にずらす
|
||
}
|
||
|
||
// 調べた結果を表示する
|
||
printf("\nData File count : %d\n", source_num);
|
||
printf("Max file size\t: %I64u\n", block_size);
|
||
printf("\nListed successfully\n");
|
||
error_end:
|
||
if (header_buf)
|
||
free(header_buf);
|
||
if (hFile != NULL)
|
||
CloseHandle(hFile);
|
||
return err;
|
||
}
|
||
|
||
// CRC-32 チェックサムを使って自分自身の破損を検出する
|
||
int par1_checksum(wchar_t *uni_buf) // 作業用
|
||
{
|
||
unsigned int crc, chk, chk2;
|
||
unsigned int len, tmp, i;
|
||
unsigned char *pAddr, *p;
|
||
HANDLE hFile, hMap;
|
||
|
||
// 実行ファイルのパスを取得する
|
||
tmp = GetModuleFileName(NULL, uni_buf, MAX_LEN);
|
||
if ((tmp == 0) || (tmp >= MAX_LEN))
|
||
return 1;
|
||
//printf("%S\n", uni_buf);
|
||
|
||
// 実行ファイルの PE checksum と CRC-32 を検証する
|
||
hFile = CreateFile(uni_buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
print_win32_err();
|
||
return 1;
|
||
}
|
||
len = GetFileSize(hFile, &chk2);
|
||
if (len == INVALID_FILE_SIZE)
|
||
return 1;
|
||
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, chk2, len, NULL);
|
||
if (hMap == NULL){
|
||
CloseHandle(hFile);
|
||
return 1;
|
||
}
|
||
pAddr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, len);
|
||
if (pAddr == NULL){
|
||
CloseHandle(hMap);
|
||
CloseHandle(hFile);
|
||
return 1;
|
||
}
|
||
if (CheckSumMappedFile(pAddr, len, &chk2, &chk) == NULL){ // PE checksum
|
||
UnmapViewOfFile(pAddr);
|
||
CloseHandle(hMap);
|
||
CloseHandle(hFile);
|
||
return 1;
|
||
}
|
||
crc = 0xFFFFFFFF;
|
||
p = pAddr;
|
||
while (len--){
|
||
tmp = (*p++);
|
||
for (i = 0; i < 8; i++){
|
||
if ((tmp ^ crc) & 1){
|
||
crc = (crc >> 1) ^ 0xEDB88320;
|
||
} else {
|
||
crc = crc >> 1;
|
||
}
|
||
tmp = tmp >> 1;
|
||
}
|
||
}
|
||
crc ^= 0xFFFFFFFF;
|
||
UnmapViewOfFile(pAddr);
|
||
CloseHandle(hMap);
|
||
CloseHandle(hFile);
|
||
|
||
if (chk != chk2)
|
||
return 2;
|
||
if (crc != 0x11111111)
|
||
return 3;
|
||
return 0;
|
||
}
|
||
|