1245 lines
51 KiB
C
1245 lines
51 KiB
C
// list.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 <process.h>
|
|
#include <stdio.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#include "common2.h"
|
|
#include "md5_crc.h"
|
|
#include "verify.h"
|
|
#include "ini.h"
|
|
#include "json.h"
|
|
#include "list.h"
|
|
|
|
//#define TIMER // 実験用
|
|
|
|
#ifdef TIMER
|
|
#include <time.h>
|
|
static double time_sec, time_speed;
|
|
#endif
|
|
|
|
// recovery set のファイルのハッシュ値を調べる (空のファイルは除く)
|
|
// 0x00 = ファイルが存在して完全である
|
|
// 0x01 = ファイルが存在しない
|
|
// 0x02 = ファイルが破損してる
|
|
// 0x10 = ファイルに追加されてる
|
|
static int check_file_hash(
|
|
char *ascii_buf, // ファイル名が入ってる
|
|
wchar_t *file_path, // 作業用
|
|
int num, // file_ctx におけるファイル番号
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
int i, bad_flag = 0;
|
|
unsigned int meta_data[7];
|
|
__int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある
|
|
HANDLE hFile;
|
|
|
|
// ファイルが存在するか
|
|
wcscpy(file_path, base_dir);
|
|
wcscpy(file_path + base_len, list_buf + files[num].name);
|
|
/*
|
|
// ディスク・キャッシュを使わない場合の実験用
|
|
i = file_hash_direct(num, file_path, list_buf + files[num].name, files, s_blk);
|
|
printf("file_hash_direct = %d\n", i);
|
|
if (i == -2)
|
|
return 2; // 検査中にキャンセルされた
|
|
*/
|
|
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない
|
|
bad_flag = 1;
|
|
} else { // ファイルの状態が記録されてるか調べる
|
|
i = check_ini_state(num, meta_data, hFile);
|
|
if (i == -1){ // エラー
|
|
CloseHandle(hFile);
|
|
bad_flag = 1; // 属性を取得できないファイルは消失と判定する
|
|
}
|
|
}
|
|
if (bad_flag == 0){ // ファイルが存在するなら状態を確認する
|
|
memcpy(&file_size, meta_data, 8);
|
|
if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる
|
|
if (i == -2){ // 検査結果の記録が無ければ
|
|
i = file_hash_check(num, list_buf + files[num].name, hFile, 0, files, s_blk);
|
|
if (i == -2)
|
|
return 2; // 検査中にキャンセルされた
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (i != -1)
|
|
write_ini_state(num, meta_data, i);
|
|
}
|
|
if (i != -3){ // ファイルのハッシュ値が異なる
|
|
bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する
|
|
if (i > 0) // 先頭に完全なブロックが見つかってるなら
|
|
bad_flag |= i << 8; // 上位 24-bit に完全なブロックの個数を記録する
|
|
// MD5-16k だけ異なるのは検出ブロック 0個として扱う
|
|
} else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ
|
|
bad_flag = 16; // 末尾にゴミが付いてないか調べる
|
|
}
|
|
} else { // file_size < files[num].size
|
|
bad_flag = 2;
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
files[num].state = bad_flag;
|
|
if (bad_flag == 1){ // 消失
|
|
printf(" - Missing : \"%s\"\n", ascii_buf);
|
|
} else { // 完全、破損または追加 (追加は特殊な破損状態)
|
|
int b_last;
|
|
if (bad_flag & 2){ // 破損なら上位 24-bit に完全なブロックの個数が記録されてる
|
|
bad_flag >>= 8;
|
|
b_last = files[num].b_off + bad_flag; // 途中まで有効と見なす
|
|
for (i = files[num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 2;
|
|
first_num += bad_flag; // ブロック数を集計する
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
|
|
printf("%13I64d %8d : \"%s\"\n", file_size, bad_flag, ascii_buf);
|
|
} else { // 完全か追加
|
|
b_last = files[num].b_off + files[num].b_num;
|
|
for (i = files[num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 1; // 全ブロックが有効と見なす
|
|
first_num += files[num].b_num; // ブロック数を集計する
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
|
|
if (bad_flag == 0){ // サイズが同じでハッシュ値が一致したら完全
|
|
printf(" = Complete : \"%s\"\n", ascii_buf);
|
|
} else { // 本来のサイズまでハッシュ値が一致したら追加
|
|
printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ブロックのチェックサムが欠落してるファイルを検査する
|
|
// 0x80 = ファイルが存在して完全である (全てのブロックが完全とみなす)
|
|
// 0x81 = ファイルが存在しない
|
|
// 0x82 = ファイルが破損してる
|
|
// 0x90 = ファイルに追加されてる (全てのブロックが完全とみなす)
|
|
int check_file_hash_incomplete(
|
|
char *ascii_buf, // ファイル名が入ってる
|
|
wchar_t *file_path, // 作業用
|
|
int num, // file_ctx におけるファイル番号
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
int i, bad_flag = 0;
|
|
unsigned int meta_data[7];
|
|
__int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある
|
|
HANDLE hFile;
|
|
|
|
// ファイルが存在するか
|
|
wcscpy(file_path, base_dir);
|
|
wcscpy(file_path + base_len, list_buf + files[num].name);
|
|
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない
|
|
bad_flag = 1;
|
|
} else { // ファイルの状態が記録されてるか調べる
|
|
i = check_ini_state(num, meta_data, hFile);
|
|
if (i == -1){ // エラー
|
|
CloseHandle(hFile);
|
|
bad_flag = 1; // 属性を取得できないファイルは消失と判定する
|
|
}
|
|
}
|
|
if (bad_flag == 0){ // ファイルが存在するなら状態を確認する
|
|
memcpy(&file_size, meta_data, 8);
|
|
if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる
|
|
if (i == -2){ // 検査結果の記録が無ければ
|
|
i = file_hash_check(num, list_buf + files[num].name, hFile, INT_MAX, files, NULL);
|
|
if (i == -2)
|
|
return 2; // 検査中にキャンセルされた
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (i != -1)
|
|
write_ini_state(num, meta_data, i);
|
|
}
|
|
if (i != -3){ // ファイルのハッシュ値が異なる
|
|
bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する
|
|
} else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ
|
|
bad_flag = 16; // 末尾にゴミが付いてないか調べる
|
|
}
|
|
} else { // file_size < files[num].size
|
|
bad_flag = 2;
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
files[num].state = bad_flag | 0x80; // チェックサムが存在しない印
|
|
if (bad_flag == 1){ // 消失
|
|
printf(" - Missing : \"%s\"\n", ascii_buf);
|
|
} else { // 完全、破損または追加 (追加は特殊な破損状態)
|
|
if (bad_flag & 2){ // ブロック単位の検査ができないので全て破損として扱う
|
|
printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf);
|
|
} else { // 完全か追加
|
|
// ブロック単位の検査はできないがとりあえず有効として扱う
|
|
int b_last;
|
|
b_last = files[num].b_off + files[num].b_num;
|
|
for (i = files[num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 1; // 全ブロックが有効と見なす
|
|
first_num += files[num].b_num; // ブロック数を集計する
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
|
|
if (bad_flag == 0){
|
|
printf(" = Complete : \"%s\"\n", ascii_buf);
|
|
} else {
|
|
printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// 空ファイルやフォルダが存在するか調べる
|
|
// non-recovery set のファイルが存在して破損してないかも調べる
|
|
// 0 = ファイルが存在して完全である
|
|
// 1 = ファイルが存在しない
|
|
// 2 = ファイルが破損してる
|
|
// 16 = ファイルに追加されてる
|
|
// 64 = フォルダが存在する
|
|
// 65 = フォルダが存在しない
|
|
static int check_file_exist(
|
|
char *ascii_buf, // ファイル名が入ってる
|
|
wchar_t *file_path, // 作業用
|
|
int num, // file_ctx におけるファイル番号
|
|
file_ctx_r *files) // 各ソース・ファイルの情報
|
|
{
|
|
int i, bad_flag = 0;
|
|
unsigned int meta_data[7];
|
|
__int64 file_size;
|
|
HANDLE hFile;
|
|
|
|
wcscpy(file_path, base_dir);
|
|
wcscpy(file_path + base_len, list_buf + files[num].name);
|
|
i = (int)wcslen(file_path);
|
|
if ((i > 1) && (file_path[i - 1] == '\\')){ // フォルダなら
|
|
bad_flag = 64;
|
|
i = GetFileAttributes(file_path);
|
|
if ((i == INVALID_FILE_ATTRIBUTES) || ((i & FILE_ATTRIBUTE_DIRECTORY) == 0)){
|
|
bad_flag = 65; // フォルダが存在しない
|
|
printf(" - Missing : \"%s\"\n", ascii_buf);
|
|
} else { // フォルダが存在する
|
|
printf(" 0 Complete : \"%s\"\n", ascii_buf);
|
|
}
|
|
files[num].state = bad_flag;
|
|
return 0;
|
|
}
|
|
|
|
// ファイルが存在するか
|
|
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない
|
|
bad_flag = 1;
|
|
} else {
|
|
// ファイルのサイズを調べる
|
|
i = check_ini_state(num, meta_data, hFile);
|
|
if (i == -1){ // エラー
|
|
CloseHandle(hFile);
|
|
bad_flag = 1; // 属性を取得できないファイルは消失と判定する
|
|
}
|
|
}
|
|
if (bad_flag == 0){ // ファイルが存在するなら状態を確認する
|
|
memcpy(&file_size, meta_data, 8);
|
|
if (files[num].size == 0){ // サイズが 0 だと破損しようがない
|
|
if (file_size > 0) // 末尾にゴミが付いてる
|
|
bad_flag = 16;
|
|
} else if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる
|
|
if (i == -2){ // 検査結果の記録が無ければ
|
|
i = file_hash_check(num, list_buf + files[num].name, hFile, INT_MAX, files, NULL);
|
|
if (i == -2)
|
|
return 2; // 検査中にキャンセルされた
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (i != -1)
|
|
write_ini_state(num, meta_data, i);
|
|
}
|
|
if (i != -3){ // ファイルのハッシュ値が異なる
|
|
bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する
|
|
} else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ
|
|
bad_flag = 16; // 末尾にゴミが付いてないか調べる
|
|
}
|
|
} else { // file_size < files[num].size
|
|
bad_flag = 2;
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
files[num].state = bad_flag;
|
|
switch (bad_flag){
|
|
case 0: // 完全なファイル
|
|
printf(" = Complete : \"%s\"\n", ascii_buf);
|
|
break;
|
|
case 1: // 消失したファイル
|
|
printf(" - Missing : \"%s\"\n", ascii_buf);
|
|
break;
|
|
case 2: // 破損したファイル
|
|
printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf);
|
|
break;
|
|
case 16: // 追加されたファイル
|
|
printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ソース・ファイルが完全かどうかを調べる
|
|
// 0=完了, 1=エラー, 2=キャンセル
|
|
int check_file_complete(
|
|
char *ascii_buf,
|
|
wchar_t *file_path, // 作業用
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
int i, rv;
|
|
#ifdef TIMER
|
|
clock_t time_start = clock();
|
|
#endif
|
|
|
|
printf("\nVerifying Input File :\n");
|
|
printf(" Size Status : Filename\n");
|
|
fflush(stdout);
|
|
count_last = 0;
|
|
first_num = 0; // 初めて見つけたソース・ブロックの数
|
|
for (i = 0; i < entity_num; i++){
|
|
if (cancel_progress() != 0) // キャンセル処理
|
|
return 2;
|
|
utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output);
|
|
|
|
if (files[i].size == 0){ // フォルダまたは空ファイルが存在するか調べる
|
|
rv = check_file_exist(ascii_buf, file_path, i, files);
|
|
} else {
|
|
if (files[i].state & 0x80){ // チェックサムが欠落してても完全かどうかは判る
|
|
rv = check_file_hash_incomplete(ascii_buf, file_path, i, files, s_blk);
|
|
} else { // ソース・ファイルの状態 (完全、消失、破損) を判定する
|
|
rv = check_file_hash(ascii_buf, file_path, i, files, s_blk);
|
|
}
|
|
}
|
|
if (rv != 0)
|
|
return rv;
|
|
fflush(stdout);
|
|
}
|
|
for (i = entity_num; i < file_num; i++){
|
|
if (cancel_progress() != 0) // キャンセル処理
|
|
return 2;
|
|
utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output);
|
|
if (rv = check_file_exist(ascii_buf, file_path, i, files))
|
|
return rv;
|
|
fflush(stdout);
|
|
}
|
|
|
|
#ifdef TIMER
|
|
time_start = clock() - time_start;
|
|
time_sec = (double)time_start / CLOCKS_PER_SEC;
|
|
if (time_sec > 0){
|
|
time_speed = (double)total_file_size / (time_sec * 1048576);
|
|
} else {
|
|
time_speed = 0;
|
|
}
|
|
printf("\n hash %.3f sec, %.0f MB/s\n", time_sec, time_speed);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
// SSD 上で複数ファイルを同時に検査する
|
|
|
|
// MAX_MULTI_READ の2倍ぐらいにする?
|
|
#define MAX_READ_NUM 12
|
|
|
|
int check_file_complete_multi(
|
|
char *ascii_buf,
|
|
wchar_t *file_path, // 作業用
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
int err = 0, i, rv;
|
|
int num, bad_flag, multi_read, multi_num, id, id_prog;
|
|
unsigned int time_last;
|
|
__int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある
|
|
HANDLE hFile;
|
|
HANDLE hSub[MAX_READ_NUM];
|
|
FILE_CHECK_TH th[MAX_READ_NUM];
|
|
#ifdef TIMER
|
|
clock_t time_start = clock();
|
|
#endif
|
|
|
|
memset(hSub, 0, sizeof(HANDLE) * MAX_READ_NUM);
|
|
// Core数に応じてスレッド数を増やす
|
|
if ((memory_use & 32) != 0){ // NVMe SSD
|
|
multi_read = (cpu_num + 2) / 3 + 1; // 3=2, 4~6=3, 7~9=4, 10~12=5, 13~=6
|
|
if (multi_read > MAX_READ_NUM / 2)
|
|
multi_read = MAX_READ_NUM / 2;
|
|
} else { // SATA SSD
|
|
multi_read = 2;
|
|
}
|
|
if (multi_read > entity_num)
|
|
multi_read = entity_num;
|
|
multi_num = 0;
|
|
#ifdef TIMER
|
|
printf("\n cpu_num = %d, entity_num = %d, multi_read = %d\n", cpu_num, entity_num, multi_read);
|
|
#endif
|
|
id_prog = -1;
|
|
for (i = 0; i < MAX_READ_NUM; i++)
|
|
th[i].num = -1; // データ未使用の印
|
|
wcscpy(file_path, base_dir);
|
|
|
|
printf("\nVerifying Input File :\n");
|
|
printf(" Size Status : Filename\n");
|
|
fflush(stdout);
|
|
time_last = GetTickCount();
|
|
count_last = 0;
|
|
first_num = 0; // 初めて見つけたソース・ブロックの数
|
|
for (num = 0; num < entity_num; num++){
|
|
if (cancel_progress() != 0){ // キャンセル処理
|
|
err = 2;
|
|
goto error_end;
|
|
}
|
|
|
|
// ソース・ファイルの状態 (完全、消失、破損) を判定する
|
|
// チェックサムが欠落してても完全かどうかは判る
|
|
if (files[num].size > 0){
|
|
for (id = 0; id < MAX_READ_NUM; id++){ // 未使用のスレッドを探す
|
|
if (th[id].num < 0)
|
|
break;
|
|
}
|
|
if (id_prog < 0){
|
|
id_prog = id;
|
|
prog_last = -1;
|
|
}
|
|
th[id].num = num;
|
|
hSub[id] = NULL;
|
|
|
|
// 検査以外はシングル・スレッドで行う
|
|
th[id].flag = 0;
|
|
wcscpy(file_path + base_len, list_buf + files[num].name);
|
|
hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない
|
|
th[id].flag = 1;
|
|
} else { // ファイルの状態が記録されてるか調べる
|
|
th[id].rv = check_ini_state(num, th[id].meta, hFile);
|
|
if (th[id].rv == -1){ // エラー
|
|
CloseHandle(hFile);
|
|
th[id].flag = 1; // 属性を取得できないファイルは消失と判定する
|
|
}
|
|
}
|
|
if (th[id].flag == 0){ // ファイルが存在するなら状態を確認する
|
|
memcpy(&file_size, th[id].meta, 8);
|
|
if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる
|
|
if (th[id].rv == -2){ // 検査結果の記録が無ければ
|
|
//rv = file_hash_check(num, list_buf + files[num].name, hFile, 0, files, s_blk);
|
|
th[id].hFile = hFile;
|
|
th[id].files = files;
|
|
if (files[num].state & 0x80){
|
|
th[id].flag |= 0x80;
|
|
th[id].s_blk = NULL;
|
|
} else {
|
|
th[id].s_blk = s_blk;
|
|
}
|
|
th[id].rv = 0;
|
|
//_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する
|
|
hSub[id] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, file_hash_background, (LPVOID)&(th[id]), 0, NULL);
|
|
if (hSub[id] == NULL){
|
|
print_win32_err();
|
|
printf("error, sub-thread\n");
|
|
CloseHandle(hFile);
|
|
err = 1;
|
|
goto error_end;
|
|
}
|
|
multi_num++;
|
|
} else {
|
|
CloseHandle(hFile);
|
|
}
|
|
} else { // file_size < files[num].size
|
|
CloseHandle(hFile);
|
|
if (files[num].state & 0x80){
|
|
th[id].flag = 2 | 0x80;
|
|
} else {
|
|
th[id].flag = 2;
|
|
}
|
|
}
|
|
}
|
|
//printf("num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog);
|
|
}
|
|
|
|
id = id_prog;
|
|
//printf("check: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog);
|
|
while ((id >= 0) && (id < MAX_READ_NUM)){
|
|
if (hSub[id]){ // 終了してるか調べる
|
|
rv = WaitForSingleObject(hSub[id], 0);
|
|
if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる
|
|
CloseHandle(hSub[id]);
|
|
hSub[id] = NULL;
|
|
multi_num--;
|
|
}
|
|
}
|
|
if (hSub[id] == NULL){
|
|
//printf("id = %d, th[id].rv = %d, th[id].flag = %d\n", id, th[id].rv, th[id].flag);
|
|
bad_flag = th[id].flag & ~0x80;
|
|
if (bad_flag != 1){ // 消失以外
|
|
memcpy(&file_size, th[id].meta, 8);
|
|
if (bad_flag != 2){ // 破損以外
|
|
rv = th[id].rv;
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (rv != -1)
|
|
write_ini_state(th[id].num, th[id].meta, rv);
|
|
if (rv != -3){ // ファイルのハッシュ値が異なる
|
|
bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する
|
|
if (rv > 0) // 先頭に完全なブロックが見つかってるなら
|
|
bad_flag |= rv << 8; // 上位 24-bit に完全なブロックの個数を記録する
|
|
// MD5-16k だけ異なるのは検出ブロック 0個として扱う
|
|
} else if (file_size > files[th[id].num].size){ // 本来のサイズまでのハッシュ値は同じ
|
|
bad_flag = 16; // 末尾にゴミが付いてないか調べる
|
|
}
|
|
}
|
|
}
|
|
|
|
utf16_to_cp(list_buf + files[th[id].num].name, ascii_buf, cp_output);
|
|
if (th[id].flag & 0x80){
|
|
files[th[id].num].state = bad_flag | 0x80; // チェックサムが存在しない印
|
|
} else {
|
|
files[th[id].num].state = bad_flag;
|
|
}
|
|
if (bad_flag == 1){ // 消失
|
|
printf(" - Missing : \"%s\"\n", ascii_buf);
|
|
} else { // 完全、破損または追加 (追加は特殊な破損状態)
|
|
int b_last;
|
|
if (bad_flag & 2){ // 破損なら上位 24-bit に完全なブロックの個数が記録されてる
|
|
if (th[id].flag & 0x80){ // チェックサムが存在しない場合
|
|
printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf);
|
|
} else {
|
|
bad_flag >>= 8;
|
|
b_last = files[th[id].num].b_off + bad_flag; // 途中まで有効と見なす
|
|
for (i = files[th[id].num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 2;
|
|
first_num += bad_flag; // ブロック数を集計する
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
|
|
printf("%13I64d %8d : \"%s\"\n", file_size, bad_flag, ascii_buf);
|
|
}
|
|
} else { // 完全か追加
|
|
b_last = files[th[id].num].b_off + files[th[id].num].b_num;
|
|
for (i = files[th[id].num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 1; // 全ブロックが有効と見なす
|
|
first_num += files[th[id].num].b_num; // ブロック数を集計する
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
|
|
if (bad_flag == 0){ // サイズが同じでハッシュ値が一致したら完全
|
|
printf(" = Complete : \"%s\"\n", ascii_buf);
|
|
} else { // 本来のサイズまでハッシュ値が一致したら追加
|
|
printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf);
|
|
}
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
time_last = GetTickCount();
|
|
|
|
th[id].num = -1; // 未使用に戻す
|
|
if (id == id_prog) // 経過表示中のスレッドが結果を表示したら
|
|
id_prog = -1;
|
|
}
|
|
|
|
// 動作中のスレッドが終了してるか調べる
|
|
for (id = 0; id < MAX_READ_NUM; id++){
|
|
if (th[id].num >= 0){
|
|
if (hSub[id]){
|
|
rv = WaitForSingleObject(hSub[id], 0);
|
|
if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる
|
|
CloseHandle(hSub[id]);
|
|
hSub[id] = NULL;
|
|
multi_num--;
|
|
}
|
|
}
|
|
// 経過表示してない状態なら、他の終了したスレッドの結果も表示する
|
|
if ((id_prog < 0) && (hSub[id] == NULL)){
|
|
id_prog = id;
|
|
//printf("next: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (id == MAX_READ_NUM){ // 経過表示中、または、他のスレッドが動いて無いなら
|
|
if (id_prog < 0){ // 経過表示するスレッドを指定する
|
|
for (i = 0; i < MAX_READ_NUM; i++){
|
|
if (th[i].num >= 0){
|
|
id_prog = i;
|
|
prog_last = -1;
|
|
break;
|
|
}
|
|
}
|
|
if (id_prog < 0) // 動作中のスレッドがなければ待たない
|
|
break;
|
|
}
|
|
// 最後のファイル、または、同時検査数や最大スレッド数に達してるなら、終わるのを待つ
|
|
for (i = 0; i < MAX_READ_NUM; i++){
|
|
if (th[i].num < 0)
|
|
break;
|
|
}
|
|
if ((num + 1 == entity_num) || (multi_num == multi_read) || (i == MAX_READ_NUM)){
|
|
id = id_prog;
|
|
//printf("wait: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog);
|
|
if (hSub[id]){ // 終了してるか調べる
|
|
rv = WaitForSingleObject(hSub[id], UPDATE_TIME / 2);
|
|
if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる
|
|
CloseHandle(hSub[id]);
|
|
hSub[id] = NULL;
|
|
multi_num--;
|
|
}
|
|
// 経過表示
|
|
if (GetTickCount() - time_last >= UPDATE_TIME){
|
|
// 上位 12-bit にパーセント、下位 20-bit に検出ブロック数が記録されてる
|
|
i = th[id].rv;
|
|
rv = i >> 20;
|
|
i = first_num + (i & 0x000FFFFF);
|
|
if (print_progress_file(rv, i, list_buf + files[th[id].num].name)){
|
|
err = 2;
|
|
goto error_end;
|
|
}
|
|
time_last = GetTickCount();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (num = 0; num < entity_num; num++){
|
|
if (files[num].size > 0)
|
|
continue;
|
|
if (cancel_progress() != 0){ // キャンセル処理
|
|
err = 2;
|
|
goto error_end;
|
|
}
|
|
// フォルダまたは空ファイルが存在するか調べる
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output);
|
|
err = check_file_exist(ascii_buf, file_path, num, files);
|
|
if (err)
|
|
goto error_end;
|
|
fflush(stdout);
|
|
}
|
|
for (num = entity_num; num < file_num; num++){
|
|
if (cancel_progress() != 0){ // キャンセル処理
|
|
err = 2;
|
|
goto error_end;
|
|
}
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output);
|
|
err = check_file_exist(ascii_buf, file_path, num, files);
|
|
if (err)
|
|
goto error_end;
|
|
fflush(stdout);
|
|
}
|
|
|
|
#ifdef TIMER
|
|
time_start = clock() - time_start;
|
|
time_sec = (double)time_start / CLOCKS_PER_SEC;
|
|
if (time_sec > 0){
|
|
time_speed = (double)total_file_size / (time_sec * 1048576);
|
|
} else {
|
|
time_speed = 0;
|
|
}
|
|
printf("\n hash %.3f sec, %.0f MB/s\n", time_sec, time_speed);
|
|
#endif
|
|
|
|
error_end:
|
|
if (err != 0){ // エラーが発生した場合
|
|
for (i = 0; i < MAX_READ_NUM; i++) // 終了指示をだす
|
|
InterlockedExchange(&(th[i].flag), -1);
|
|
}
|
|
for (i = 0; i < MAX_READ_NUM; i++){
|
|
if (hSub[i]){
|
|
WaitForSingleObject(hSub[i], INFINITE);
|
|
CloseHandle(hSub[i]);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
// 指定されたフォルダが空かどうかを調べる
|
|
static int is_folder_empty(wchar_t *folder_path) // フォルダのパス
|
|
{
|
|
int len;
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
len = (int)wcslen(folder_path);
|
|
folder_path[len ] = '*';
|
|
folder_path[len + 1] = 0;
|
|
hFind = FindFirstFile(folder_path, &FindData);
|
|
folder_path[len ] = 0; // 元に戻す
|
|
if (hFind != INVALID_HANDLE_VALUE){
|
|
do {
|
|
if ((wcscmp(FindData.cFileName, L".") != 0) && (wcscmp(FindData.cFileName, L"..") != 0)){
|
|
len = 0; // 空ではない
|
|
break;
|
|
}
|
|
} while (FindNextFile(hFind, &FindData));
|
|
}
|
|
FindClose(hFind);
|
|
|
|
return len;
|
|
}
|
|
|
|
// 指定フォルダ内の別名ファイルを探す
|
|
// 0=正常終了, 1=エラー, 2=キャンセル
|
|
static int recursive_search(
|
|
char *ascii_buf, // 作業用
|
|
wchar_t *search_path, // 検索するファイルの親ディレクトリ、見つけた別名ファイルの相対パスが戻る
|
|
int dir_len, // ディレクトリ部分の長さ
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
__int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある
|
|
int rv, i, num, b_last, source_len;
|
|
unsigned int meta_data[7];
|
|
HANDLE hFile, hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
// ソース・ファイル名の領域までしか比較しない (別名ファイルが検出されるのは一回だけ)
|
|
source_len = list_len - 1;
|
|
|
|
// 指定されたディレクトリ直下のファイルと比較する
|
|
wcscpy(search_path + dir_len, L"*");
|
|
hFind = FindFirstFile(search_path, &FindData);
|
|
if (hFind != INVALID_HANDLE_VALUE){
|
|
do {
|
|
if (cancel_progress() != 0){ // キャンセル処理
|
|
FindClose(hFind);
|
|
return 2;
|
|
}
|
|
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0){ // フォルダなら
|
|
// 親ディレクトリは無視する
|
|
if ((wcscmp(FindData.cFileName, L".") == 0) || (wcscmp(FindData.cFileName, L"..") == 0))
|
|
continue;
|
|
// 発見したフォルダ名が長すぎる場合は無視する
|
|
if (dir_len + wcslen(FindData.cFileName) + 2 >= MAX_LEN) // 末尾に「\*」が追加される
|
|
continue;
|
|
// 現在のディレクトリ部分に見つかったフォルダ名を連結する
|
|
wcscpy(search_path + dir_len, FindData.cFileName);
|
|
wcscat(search_path + dir_len, L"\\");
|
|
// 他のソース・ファイルは無視する
|
|
if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する
|
|
continue;
|
|
|
|
// フォルダ名が検出対象と一致するかどうか確かめる
|
|
for (num = entity_num; num < file_num; num++){ // 空フォルダは non-recovery set にしか存在しない
|
|
if (files[num].state == 65){ // 消失したフォルダなら
|
|
if (_wcsicmp(search_path + dir_len, offset_file_name(list_buf + files[num].name)) == 0){
|
|
// 他のファイルのサブ・ディレクトリは動かせないので、中身のあるフォルダは無視する
|
|
if (is_folder_empty(search_path) == 0)
|
|
continue;
|
|
// 移動されたフォルダが見つかった
|
|
files[num].name2 = list_len; // 移動されたフォルダ名を記録する
|
|
if (add_file_path(search_path + base_len)){
|
|
printf("add_file_path\n");
|
|
FindClose(hFind);
|
|
return 1;
|
|
}
|
|
utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 移動されたフォルダ名を表示する
|
|
printf(" 0 Found : \"%s\"\n", ascii_buf);
|
|
files[num].state = 64 | 32; // 移動されたフォルダ
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のフォルダ名を表示する
|
|
printf(" = Moved : \"%s\"\n", ascii_buf);
|
|
break; // 見つかった場合は、それ以降のソース・ファイルと比較しない
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// 発見したファイル名が長すぎる場合は無視する
|
|
if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN)
|
|
continue;
|
|
// 現在のディレクトリ部分に見つかったファイル名を連結する
|
|
wcscpy(search_path + dir_len, FindData.cFileName);
|
|
// 破損してないリカバリ・ファイルは無視する
|
|
if (search_file_path(recv_buf, recv_len, search_path)) // フル・パスで比較する
|
|
continue;
|
|
// 他のソース・ファイルは無視する
|
|
if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する
|
|
continue;
|
|
|
|
// ファイル・サイズが検出対象のサイズと一致するかどうか
|
|
file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow);
|
|
for (num = 0; num < file_num; num++){
|
|
if ((files[num].state & 0x03) && (files[num].size == file_size)){
|
|
if (file_size > 0){
|
|
//printf("find size %I64d, %S\n", file_size, search_path + base_len);
|
|
// 別名ファイルのハッシュ値を調べる
|
|
hFile = CreateFile(search_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を確認する
|
|
prog_last = -1; // 経過表示がまだの印
|
|
rv = check_ini_state(num, meta_data, hFile);
|
|
if (rv == -2){ // 検査結果の記録が無ければ
|
|
if ((num >= entity_num) || ((files[num].state & 0x80) != 0)){
|
|
// non-recovery set のファイルまたはチェックサムが欠落したソース・ファイル
|
|
rv = file_hash_check(num, FindData.cFileName, hFile, INT_MAX, files, NULL);
|
|
} else { // 普通のソース・ファイル
|
|
if (files[num].state & 0x02){ // 本来のファイルが破損ならスライスの重複カウントを避ける
|
|
rv = files[num].state >> 8;
|
|
} else {
|
|
rv = 0;
|
|
}
|
|
rv = file_hash_check(num, FindData.cFileName, hFile, rv, files, s_blk);
|
|
}
|
|
if (rv == -2){
|
|
CloseHandle(hFile);
|
|
FindClose(hFind);
|
|
return 2; // エラーは無視して続行するが、キャンセルなら中断する
|
|
}
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (rv != -1)
|
|
write_ini_state(num, meta_data, rv);
|
|
}
|
|
CloseHandle(hFile);
|
|
if (rv == -3){ // ファイルのハッシュ値が一致した
|
|
b_last = files[num].b_off + files[num].b_num;
|
|
for (i = files[num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 1; // 全ブロックが有効と見なす
|
|
files[num].name2 = list_len; // 別名・移動のファイル名を記録する
|
|
if (add_file_path(search_path + base_len)){
|
|
printf("add_file_path\n");
|
|
FindClose(hFind);
|
|
return 1;
|
|
}
|
|
if (files[num].state & 0x01){ // 本来のファイルが消失してるなら
|
|
files[num].state ^= 0x21; // 消失して別名・移動 0x01 -> 0x20
|
|
first_num += files[num].b_num; // ブロック数を集計する
|
|
} else {
|
|
files[num].state ^= 0x2A; // 破損して別名・移動 0x02 -> 0x28 (スライス個数は維持する)
|
|
first_num += files[num].b_num - (files[num].state >> 8); // 新たなブロック数だけ追加する
|
|
}
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する
|
|
utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 別名・移動のファイル名を表示する
|
|
printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf);
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する
|
|
if (compare_directory(list_buf + files[num].name, search_path + base_len) == 0){ // 同じ場所なら別名
|
|
printf(" = Misnamed : \"%s\"\n", ascii_buf);
|
|
} else { // 別の場所なら移動
|
|
printf(" = Moved : \"%s\"\n", ascii_buf);
|
|
}
|
|
break; // 見つかった場合は、それ以降のソース・ファイルと比較しない
|
|
} else {
|
|
if (rv > 0){ // 別名の破損ファイル内にスライスを見つけたら、自動的に検査対象に追加する
|
|
//printf("rv = %d, %S\n", rv, search_path + base_len);
|
|
if (list2_buf == NULL){
|
|
list2_len = 0;
|
|
list2_max = ALLOC_LEN;
|
|
list2_buf = (wchar_t *)malloc(list2_max * 2);
|
|
}
|
|
if (list2_buf){
|
|
wchar_t *tmp_p;
|
|
int len = (int)wcslen(search_path);
|
|
if (!search_file_path(list2_buf, list2_len, search_path)){ // ファイル名が重複しないようにする
|
|
if (list2_len + len >= list2_max){ // 領域が足りなくなるなら拡張する
|
|
list2_max += ALLOC_LEN;
|
|
tmp_p = (wchar_t *)realloc(list2_buf, list2_max * 2);
|
|
if (tmp_p == NULL){
|
|
printf("realloc, %d\n", list2_max);
|
|
FindClose(hFind);
|
|
return 1;
|
|
} else {
|
|
list2_buf = tmp_p;
|
|
}
|
|
}
|
|
// そのファイルを追加する
|
|
//printf_cp("add external file, %s\n", search_path);
|
|
wcscpy(list2_buf + list2_len, search_path);
|
|
list2_len += len + 1;
|
|
}
|
|
}
|
|
if (prog_last >= 0) // 途中までのスライス数を表示してた場合
|
|
print_progress_file(-1, first_num, NULL); // 元の数に戻しておく
|
|
}
|
|
print_progress_done(); // 経過表示があれば 100% にしておく
|
|
}
|
|
}
|
|
} else { // サイズが 0 (空ファイル) の場合
|
|
if (files[num].state & 64)
|
|
continue; // 比較対象がフォルダなら無視する (ファイル名の比較ではねられるから不要?)
|
|
// ファイル名が同じかどうか確かめる
|
|
if (_wcsicmp(FindData.cFileName, offset_file_name(list_buf + files[num].name)) == 0){
|
|
// 移動されたファイルが見つかった
|
|
files[num].name2 = list_len; // 移動されたファイル名を記録する
|
|
if (add_file_path(search_path + base_len)){
|
|
printf("add_file_path\n");
|
|
FindClose(hFind);
|
|
return 1;
|
|
}
|
|
utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 移動されたファイル名を表示する
|
|
printf(" 0 Found : \"%s\"\n", ascii_buf);
|
|
files[num].state = 0x20; // 空ファイルは破損しないので、消失して移動のみ
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する
|
|
printf(" = Moved : \"%s\"\n", ascii_buf);
|
|
break; // 見つかった場合は、それ以降のソース・ファイルと比較しない
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
|
|
}
|
|
FindClose(hFind);
|
|
|
|
// 指定されたディレクトリ以下のフォルダ内も再帰的に調べる
|
|
wcscpy(search_path + dir_len, L"*");
|
|
hFind = FindFirstFile(search_path, &FindData);
|
|
if (hFind != INVALID_HANDLE_VALUE){
|
|
do {
|
|
// フォルダ以外は無視する
|
|
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
continue;
|
|
// 親ディレクトリは無視する
|
|
if ((wcscmp(FindData.cFileName, L".") == 0) || (wcscmp(FindData.cFileName, L"..") == 0))
|
|
continue;
|
|
// 発見したフォルダ名が長すぎる場合は無視する
|
|
if (dir_len + wcslen(FindData.cFileName) + 2 >= MAX_LEN) // 末尾に「\*」が追加される
|
|
continue;
|
|
// そのフォルダの内部を新たな検索対象にする
|
|
wcscpy(search_path + dir_len, FindData.cFileName);
|
|
wcscat(search_path + dir_len, L"\\"); // ディレクトリ記号は「\」に統一してある
|
|
//printf_cp("inner search... %s\n", search_path + base_len);
|
|
rv = recursive_search(ascii_buf, search_path, (int)wcslen(search_path), files, s_blk);
|
|
if (rv != 0){
|
|
// エラー発生、またはキャンセルされた
|
|
FindClose(hFind);
|
|
return rv;
|
|
}
|
|
} while (FindNextFile(hFind, &FindData)); // 次のフォルダを検索する
|
|
}
|
|
FindClose(hFind);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// フォルダを無視して、指定フォルダ内の別名ファイルを探す(サブ・フォルダに移動したファイルは検出不能)
|
|
// 0=正常終了, 1=エラー, 2=キャンセル
|
|
static int direct_search(
|
|
char *ascii_buf, // 作業用
|
|
wchar_t *search_path, // 検索するファイルの親ディレクトリ、見つけた別名ファイルの相対パスが戻る
|
|
int dir_len, // ディレクトリ部分の長さ
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
__int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある
|
|
int rv, i, num, b_last, source_len;
|
|
unsigned int meta_data[7];
|
|
HANDLE hFile, hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
// ソース・ファイル名の領域までしか比較しない (別名ファイルが検出されるのは一回だけ)
|
|
source_len = list_len - 1;
|
|
|
|
// 指定されたディレクトリ直下のファイルとだけ比較する
|
|
wcscpy(search_path + dir_len, L"*");
|
|
hFind = FindFirstFile(search_path, &FindData);
|
|
if (hFind != INVALID_HANDLE_VALUE){
|
|
do {
|
|
if (cancel_progress() != 0){ // キャンセル処理
|
|
FindClose(hFind);
|
|
return 2;
|
|
}
|
|
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) // フォルダなら無視する
|
|
continue;
|
|
|
|
// 発見したファイル名が長すぎる場合は無視する
|
|
if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN)
|
|
continue;
|
|
// 現在のディレクトリ部分に見つかったファイル名を連結する
|
|
wcscpy(search_path + dir_len, FindData.cFileName);
|
|
// 破損してないリカバリ・ファイルは無視する
|
|
if (search_file_path(recv_buf, recv_len, search_path)) // フル・パスで比較する
|
|
continue;
|
|
// 他のソース・ファイルは無視する
|
|
if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する
|
|
continue;
|
|
|
|
// ファイル・サイズが検出対象のサイズと一致するかどうか
|
|
file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow);
|
|
for (num = 0; num < file_num; num++){
|
|
if ((files[num].state & 0x03) && (files[num].size == file_size)){
|
|
if (file_size > 0){ // サイズが 0 (空ファイル) でなければ
|
|
//printf("find size %I64d, %S\n", file_size, search_path + base_len);
|
|
// 別名ファイルのハッシュ値を調べる
|
|
hFile = CreateFile(search_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL);
|
|
if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を確認する
|
|
prog_last = -1; // 経過表示がまだの印
|
|
rv = check_ini_state(num, meta_data, hFile);
|
|
if (rv == -2){ // 検査結果の記録が無ければ
|
|
if ((num >= entity_num) || ((files[num].state & 0x80) != 0)){
|
|
// non-recovery set のファイルまたはチェックサムが欠落したソース・ファイル
|
|
rv = file_hash_check(num, FindData.cFileName, hFile, INT_MAX, files, NULL);
|
|
} else { // 普通のソース・ファイル
|
|
if (files[num].state & 0x02){ // 本来のファイルが破損ならスライスの重複カウントを避ける
|
|
rv = files[num].state >> 8;
|
|
} else {
|
|
rv = 0;
|
|
}
|
|
rv = file_hash_check(num, FindData.cFileName, hFile, rv, files, s_blk);
|
|
}
|
|
if (rv == -2){
|
|
CloseHandle(hFile);
|
|
FindClose(hFind);
|
|
return 2; // エラーは無視して続行するが、キャンセルなら中断する
|
|
}
|
|
// MD5-16k が一致した時だけ検査結果を記録する
|
|
if (rv != -1)
|
|
write_ini_state(num, meta_data, rv);
|
|
}
|
|
CloseHandle(hFile);
|
|
if (rv == -3){ // ファイルのハッシュ値が一致した
|
|
b_last = files[num].b_off + files[num].b_num;
|
|
for (i = files[num].b_off; i < b_last; i++)
|
|
s_blk[i].exist = 1; // 全ブロックが有効と見なす
|
|
files[num].name2 = list_len; // 別名・移動のファイル名を記録する
|
|
if (add_file_path(search_path + base_len)){
|
|
printf("add_file_path\n");
|
|
FindClose(hFind);
|
|
return 1;
|
|
}
|
|
if (files[num].state & 0x01){ // 本来のファイルが消失してるなら
|
|
files[num].state ^= 0x21; // 消失して別名・移動 0x01 -> 0x20
|
|
first_num += files[num].b_num; // ブロック数を集計する
|
|
} else {
|
|
files[num].state ^= 0x2A; // 破損して別名・移動 0x02 -> 0x28 (スライス個数は維持する)
|
|
first_num += files[num].b_num - (files[num].state >> 8); // 新たなブロック数だけ追加する
|
|
}
|
|
print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する
|
|
utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 別名・移動のファイル名を表示する
|
|
printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf);
|
|
utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する
|
|
if (compare_directory(list_buf + files[num].name, search_path + base_len) == 0){ // 同じ場所なら別名
|
|
printf(" = Misnamed : \"%s\"\n", ascii_buf);
|
|
} else { // 別の場所なら移動
|
|
printf(" = Moved : \"%s\"\n", ascii_buf);
|
|
}
|
|
break; // 見つかった場合は、それ以降のソース・ファイルと比較しない
|
|
} else {
|
|
if (rv > 0){ // 別名の破損ファイル内にスライスを見つけたら、自動的に検査対象に追加する
|
|
//printf("rv = %d, %S\n", rv, search_path + base_len);
|
|
if (list2_buf == NULL){
|
|
list2_len = 0;
|
|
list2_max = ALLOC_LEN;
|
|
list2_buf = (wchar_t *)malloc(list2_max * 2);
|
|
}
|
|
if (list2_buf){
|
|
wchar_t *tmp_p;
|
|
int len = (int)wcslen(search_path);
|
|
if (!search_file_path(list2_buf, list2_len, search_path)){ // ファイル名が重複しないようにする
|
|
if (list2_len + len >= list2_max){ // 領域が足りなくなるなら拡張する
|
|
list2_max += ALLOC_LEN;
|
|
tmp_p = (wchar_t *)realloc(list2_buf, list2_max * 2);
|
|
if (tmp_p == NULL){
|
|
printf("realloc, %d\n", list2_max);
|
|
FindClose(hFind);
|
|
return 1;
|
|
} else {
|
|
list2_buf = tmp_p;
|
|
}
|
|
}
|
|
// そのファイルを追加する
|
|
//printf_cp("add external file, %s\n", search_path);
|
|
wcscpy(list2_buf + list2_len, search_path);
|
|
list2_len += len + 1;
|
|
}
|
|
}
|
|
if (prog_last >= 0) // 途中までのスライス数を表示してた場合
|
|
print_progress_file(-1, first_num, NULL); // 元の数に戻しておく
|
|
}
|
|
print_progress_done(); // 経過表示があれば 100% にしておく
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
|
|
}
|
|
FindClose(hFind);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ソース・ファイルが不完全なら別名・移動ファイルを探す
|
|
// 0=完了, 1=エラー, 2=キャンセル
|
|
int search_misnamed_file(
|
|
char *ascii_buf, // 作業用
|
|
wchar_t *file_path, // 作業用
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
wchar_t find_path[MAX_LEN];
|
|
int i, rv, num, b_last;
|
|
|
|
// 順列検査でファイルが破損してれば上書修復する(作業ファイルを作らない)
|
|
if (switch_v & 4){
|
|
for (num = 0; num < entity_num; num++){
|
|
if (files[num].state & 2)
|
|
files[num].state |= 4;
|
|
}
|
|
}
|
|
|
|
// 不完全なソース・ファイルが存在するか調べる
|
|
i = 0; // 消失 0x01, 0x81, フォルダの消失 0x41 も
|
|
rv = 0; // 破損 0x02, 0x82, 追加 0x10 も
|
|
b_last = 0; // 消失や破損なら別名ファイルを探す
|
|
for (num = 0; num < file_num; num++){
|
|
if (files[num].state & 0x01)
|
|
i++; // 消失したファイルの数
|
|
if (files[num].state & 0x12)
|
|
rv++; // 破損・追加のファイルの数
|
|
if (files[num].state & 0x03) // 消失 0x01、破損 0x02 なら別名ファイルを探す
|
|
b_last++; // 探す必要があるファイルの数
|
|
}
|
|
printf("\nComplete file count\t: %d\n", file_num - i - rv);
|
|
if (b_last == 0){ // 探さない場合はファイル数の集計だけ
|
|
printf("Misnamed file count\t: %d\n", 0);
|
|
printf("Damaged file count\t: %d\n", rv);
|
|
printf("Missing file count\t: %d\n", i);
|
|
return 0; // 探さないで終わる
|
|
}
|
|
|
|
// 検査したファイルごとに結果を表示する
|
|
printf("\nSearching misnamed file: %d\n", b_last); // 探す対象のファイル数
|
|
printf(" Size Status : Filename\n");
|
|
fflush(stdout);
|
|
wcscpy(find_path, base_dir);
|
|
if (switch_v & 8){ // 基準ディレクトリ直下のファイルだけを検査する
|
|
if (rv = direct_search(ascii_buf, find_path, base_len, files, s_blk))
|
|
return rv;
|
|
} else { // 基準ディレクトリ以下を再帰的に検索する
|
|
if (rv = recursive_search(ascii_buf, find_path, base_len, files, s_blk))
|
|
return rv;
|
|
}
|
|
|
|
// 状態ごとにファイル数を集計する
|
|
i = 0; // 消失 0x01, 0x81, フォルダの消失 0x41 も
|
|
rv = 0; // 破損 0x02, 0x82, 追加 0x10 も
|
|
b_last = 0; // 消失して別名・移動 0x20, 0xA0, 破損して別名・移動 0x28, 0xA8, フォルダの移動 0x60
|
|
for (num = 0; num < file_num; num++){
|
|
if (files[num].state & 0x01)
|
|
i++; // 消失したファイルの数
|
|
if (files[num].state & 0x12)
|
|
rv++; // 破損・追加のファイルの数
|
|
if (files[num].state & 0x20)
|
|
b_last++; // 別名・移動の数
|
|
}
|
|
printf("\n");
|
|
printf("Misnamed file count\t: %d\n", b_last);
|
|
printf("Damaged file count\t: %d\n", rv);
|
|
printf("Missing file count\t: %d\n", i);
|
|
return 0;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
// 破損・分割・類似名のファイルから使えるスライスを探す
|
|
// 0=完了, 1=エラー, 2=キャンセル
|
|
int search_file_slice(
|
|
char *ascii_buf, // 作業用
|
|
wchar_t *file_path, // 作業用
|
|
file_ctx_r *files, // 各ソース・ファイルの情報
|
|
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
|
{
|
|
int rv, num;
|
|
slice_ctx sc[1];
|
|
|
|
printf("\nInput File Slice found\t: %d\n", first_num);
|
|
fflush(stdout);
|
|
if (first_num > source_num)
|
|
return 1; // 検出数が多すぎるのはどこか間違ってる
|
|
if ((first_num == source_num) || (block_size <= 4))
|
|
return 0; // 探さないで終わる
|
|
|
|
// スライス検査の準備をする
|
|
memset(sc, 0, sizeof(slice_ctx));
|
|
rv = init_verification(files, s_blk, sc);
|
|
if (rv < 0) // 致命的なエラーまたはスライス検査不要
|
|
return 0;
|
|
if (rv == 1) // スライド検査不可あるいは不要
|
|
switch_v |= 1; // スライド検査しない場合は簡易検査にする
|
|
//printf("\n init_verification = %d, switch_v = %d\n", rv, switch_v);
|
|
|
|
// 検査したファイルごとに結果を表示する
|
|
printf("\nFinding available slice:\n");
|
|
printf(" Size Status : Filename\n");
|
|
fflush(stdout);
|
|
|
|
// 破損してるファイルと分割・類似名ファイルを検査する
|
|
for (num = 0; num < entity_num; num++){
|
|
// 破損 0x02、追加 0x10、破損して別名 0x28 ならソース・ファイルを検査する
|
|
// 消失 0x01、破損 0x02 で、そのファイルのスライスが未検出なら、分割ファイルを探す
|
|
// 消失で分割ファイルが見つからない場合だけ、文字化けファイルも探す
|
|
if ((files[num].state & 0x1B) == 0)
|
|
continue;
|
|
|
|
if (rv = cancel_progress()) // キャンセル処理
|
|
goto error_end;
|
|
|
|
if (rv = check_file_slice(ascii_buf, file_path, num, files, s_blk, sc))
|
|
goto error_end;
|
|
}
|
|
|
|
// 指定された外部ファイルを検査する
|
|
if ((first_num < source_num) && (list2_buf != NULL) && ((switch_v & 4) == 0)){
|
|
if (rv = check_external_file(ascii_buf, file_path, files, s_blk, sc))
|
|
goto error_end;
|
|
}
|
|
|
|
// 簡易検査と順列検査以外なら、そのファイルのスライスが全て検出されていても、分割ファイルを探す
|
|
num = 0;
|
|
if (switch_v & (1 | 4))
|
|
num = entity_num;
|
|
for (; num < entity_num; num++){
|
|
if (first_num >= source_num)
|
|
break;
|
|
|
|
if (rv = cancel_progress()) // キャンセル処理
|
|
goto error_end;
|
|
|
|
if (rv = search_file_split(ascii_buf, file_path, num, files, s_blk, sc))
|
|
goto error_end;
|
|
}
|
|
|
|
// 基準ディレクトリ内のその他のファイルを検査する
|
|
if ((first_num < source_num) && ((switch_v & 4) == 0)){
|
|
if (rv = search_additional_file(ascii_buf, file_path, files, s_blk, sc))
|
|
goto error_end;
|
|
}
|
|
|
|
// 見つかったソース・ブロックの数
|
|
printf("\nInput File Slice found\t: %d\n", first_num);
|
|
fflush(stdout);
|
|
if (first_num > source_num){
|
|
rv = 1; // 検出数が多すぎるのはどこか間違ってる
|
|
} else {
|
|
rv = 0;
|
|
}
|
|
json_save_found();
|
|
|
|
error_end:
|
|
if (sc->buf){
|
|
if (sc->h){ // サブ・スレッドを終了させる
|
|
sc->size = 0; // 終了指示
|
|
SetEvent(sc->run);
|
|
WaitForSingleObject(sc->h, INFINITE);
|
|
CloseHandle(sc->h);
|
|
}
|
|
free(sc->buf);
|
|
}
|
|
if (sc->order)
|
|
free(sc->order);
|
|
if (sc->hFile_tmp)
|
|
CloseHandle(sc->hFile_tmp);
|
|
if (sc->flk_buf)
|
|
free(sc->flk_buf);
|
|
|
|
return rv;
|
|
}
|
|
|