// search.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 #include #include "common2.h" #include "crc.h" #include "md5_crc.h" #include "ini.h" #include "json.h" #include "search.h" #define MIN_PACKET_SIZE 68 // ファイル情報のファイル名の文字コードを変換しなおす static int convert_filename(unsigned int new_CP, unsigned int old_CP, file_ctx_r *files) { char ascii_buf[MAX_LEN * 3]; wchar_t uni_buf[MAX_LEN]; int i, j, len; // 全てのファイル名を変換可能かを先に確かめる for (i = 0; i < file_num; i++){ if (files[i].name <= 0) continue; // ファイル名が取得できなかったファイルはとばす // old_CP で変換されたユニコードから元のデータに戻す if (utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP)) return 1; // new_CP で再度変換しなおす if (cp_to_utf16(ascii_buf, uni_buf, new_CP)) return 1; } // 先に確認済みなので、変換エラーは発生しないはず for (i = 0; i < file_num; i++){ if (files[i].name <= 0) continue; // ファイル名が取得できなかったファイルはとばす // old_CP で変換されたユニコードから元のデータに戻す utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP); // new_CP で再度変換しなおす cp_to_utf16(ascii_buf, uni_buf, new_CP); // 変換前のファイル名をリストから取り除く len = (int)wcslen(list_buf + files[i].name); list_len = remove_file_path(list_buf, list_len, files[i].name); for (j = 0; j < file_num; j++){ if (files[j].name > files[i].name) files[j].name -= (len + 1); // リスト上で後ろのやつをずらしていく } // 変換しなおしたファイル名を追加する files[i].name = list_len; if (add_file_path(uni_buf)) return 2; } return 0; } // UTF-8 で記録しない PAR2 client かどうか static int non_utf8_client(char *creator) { int version; if (strncmp(creator, "QuickPar ", 9) == 0){ // QuickPar 0.9 version = creator[9] - 48; version *= 10; version += creator[10] - 48; if (version <= 9) return 1; } else if (strncmp(creator, "Created by phpar2 version ", 26) == 0){ // Created by phpar2 version 1.3. version = creator[26] - 48; version *= 10; version += creator[28] - 48; if (version <= 13) return 1; } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // リカバリ・ファイルを検索してファイル・リストに追加する int search_recovery_files(void) { wchar_t recovery_base[MAX_LEN], search_path[MAX_LEN], file_ext[EXT_LEN]; wchar_t *tmp_p; int len, dir_len, ext_len, l_max; HANDLE hFind; WIN32_FIND_DATA FindData; get_base_dir(recovery_file, recovery_base); dir_len = (int)wcslen(recovery_base); // リカバリ・ファイルの名前の基 get_base_filename(recovery_file, search_path, file_ext); if (switch_v & 2){ // 追加検査なら同じディレクトリ内の全ファイル wcscpy(search_path, recovery_base); wcscat(search_path, L"*"); if (file_ext[0] != 0) // 拡張子があったなら「*.拡張子」で検索する wcscat(search_path, file_ext); } else { if (file_ext[0] != 0){ // 拡張子があったなら「.*拡張子」で検索する file_ext[0] = '*'; wcscat(search_path, L"."); wcscat(search_path, file_ext); file_ext[0] = '.'; } else { wcscat(search_path, L"*"); } } ext_len = (int)wcslen(file_ext); l_max = ALLOC_LEN; recv_len = 0; recv_buf = (wchar_t *)malloc(l_max * 2); if (recv_buf == NULL){ printf("malloc, %d\n", l_max * 2); return 1; } // リカバリ・ファイルを検索する hFind = FindFirstFile(search_path, &FindData); if (hFind == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot find recovery file, %s\n", search_path); return 1; } do { if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ len = (int)wcslen(FindData.cFileName); if (dir_len + len >= MAX_LEN) continue; // 長すぎるファイル名は無視する if (file_ext[0] != 0){ // 拡張子が同じファイルだけ検査する if ((len <= ext_len) || (_wcsicmp(FindData.cFileName + (len - ext_len), file_ext) != 0)) continue; } if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する l_max += ALLOC_LEN; tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); if (tmp_p == NULL){ FindClose(hFind); printf("realloc, %d\n", l_max * 2); return 1; } else { recv_buf = tmp_p; } } // リストにコピーする wcscpy(recv_buf + recv_len, recovery_base); recv_len += dir_len; wcscpy(recv_buf + recv_len, FindData.cFileName); recv_len += len + 1; recovery_num++; } } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する FindClose(hFind); // リカバリ・ファイルの拡張子が .par2 以外なら再検索する if ((file_ext[0] != 0) && (_wcsicmp(file_ext, L".par2") != 0)){ get_base_filename(recovery_file, search_path, file_ext); wcscat(search_path, L"."); wcscat(search_path, L"*"); wcscat(search_path, L".par2"); // 「.*.par2」で検索する hFind = FindFirstFile(search_path, &FindData); if (hFind != INVALID_HANDLE_VALUE){ do { if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ len = (int)wcslen(FindData.cFileName); if (dir_len + len >= MAX_LEN) continue; // 長すぎるファイル名は無視する if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する l_max += ALLOC_LEN; tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); if (tmp_p == NULL){ FindClose(hFind); printf("realloc, %d\n", l_max * 2); return 1; } else { recv_buf = tmp_p; } } // リストにコピーする wcscpy(recv_buf + recv_len, recovery_base); recv_len += dir_len; wcscpy(recv_buf + recv_len, FindData.cFileName); recv_len += len + 1; recovery_num++; } } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する FindClose(hFind); } } if (recovery_num > 1) sort_list(recv_buf, recv_len); // リスト内でファイル名順に並び替える (外部ファイルは並び替えない) // 指定された検査対象にリカバリ・ファイルが含まれるかもしれない if (list2_buf){ int list2_off = 0; if (file_ext[0] != 0){ // リカバリ・ファイルの拡張子 ext_len = (int)wcslen(file_ext); } else { ext_len = MAX_LEN; // 拡張子が省略されてる場合は比較しない } while (list2_off < list2_len){ len = (int)wcslen(list2_buf + list2_off); // ファイル名が重複しないようにする if (search_file_path(recv_buf, recv_len, list2_buf + list2_off)){ list2_off += len + 1; continue; } // 拡張子が「.par2」あるいはリカバリ・ファイルと同じ外部ファイルだけ検査する if (((len > 5) && (_wcsicmp(list2_buf + (list2_off + len - 5), L".par2") == 0)) || ((len > ext_len) && (_wcsicmp(list2_buf + (list2_off + len - ext_len), file_ext) == 0))){ if (recv_len + len >= l_max){ // 領域が足りなくなるなら拡張する l_max += ALLOC_LEN; tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); if (tmp_p == NULL){ printf("realloc, %d\n", l_max * 2); return 1; } else { recv_buf = tmp_p; } } // リストにコピーする wcscpy(recv_buf + recv_len, list2_buf + list2_off); recv_len += len + 1; recovery_num++; } list2_off += len + 1; // 次のファイルへ } } // 探査時に除外するリストの領域を確保しておく recv2_len = 0; recv2_buf = (wchar_t *)malloc(recv_len * 2); if (recv2_buf == NULL){ printf("malloc, %d\n", recv_len * 2); return 1; } /*{ FILE *fp; fp = fopen("par_list.txt", "wb"); fwrite(recv_buf, 2, recv_len, fp); fclose(fp); }*/ return 0; } // Magic sequence と一致するか (match = 0, fail = 1) static int match_magic(unsigned char *buf) { if ((*buf++) != 'P') return 1; if ((*buf++) != 'A') return 1; if ((*buf++) != 'R') return 1; if ((*buf++) != '2') return 1; if ((*buf++) != 0) return 1; if ((*buf++) != 'P') return 1; if ((*buf++) != 'K') return 1; if ((*buf++) != 'T') return 1; return 0; } // Main packet を末尾から遡って探す int search_main_packet( unsigned char *buf, // 作業バッファー、File ID が戻る unsigned char *set_id) // Recovery Set ID が戻る { unsigned char hash[16]; int num, recv_off, len, off; unsigned int rv, packet_size, time_last; __int64 file_size, file_off, end_size; HANDLE hFile; // リカバリ・ファイルの一覧を表示する printf("PAR File list :\n"); printf(" Size : Filename\n"); total_file_size = 0; recv_off = 0; while (recv_off < recv_len){ // ファイルを開くことができるか、サイズを取得できるかを確かめる hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE){ recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く recovery_num--; continue; } // ファイルのサイズを取得する if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ CloseHandle(hFile); recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く recovery_num--; continue; } CloseHandle(hFile); total_file_size += file_size; // リカバリ・ファイルの名前 if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら utf16_to_cp(offset_file_name(recv_buf + recv_off), buf, cp_output); // ファイル名だけにする } else { path_to_cp(recv_buf + recv_off, buf, cp_output); // パスも表示する } printf("%13I64d : \"%s\"\n", file_size, buf); while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす recv_off++; recv_off++; } printf("\nPAR File total size\t: %I64d\n", total_file_size); printf("PAR File possible count\t: %d\n\n", recovery_num); recv_off = 0; // 先頭に戻しておく prog_last = -1; end_size = 0; for (num = 0; num < recovery_num; num++){ if (cancel_progress() != 0) // キャンセル処理 return 2; // リカバリ・ファイルを開く if (num == 0){ // まずは指定されたリカバリ・ファイルから探す hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } else { if (_wcsicmp(recv_buf + recv_off, recovery_file) == 0){ // 最初に検査したリカバリ・ファイルはとばす while (recv_buf[recv_off] != 0) recv_off++; recv_off++; } hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); while (recv_buf[recv_off] != 0) recv_off++; recv_off++; } if (hFile == INVALID_HANDLE_VALUE) continue; // エラーが発生したら次のリカバリ・ファイルを調べる // ファイルのサイズを取得する if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ CloseHandle(hFile); continue; // エラーが発生したら次のリカバリ・ファイルを調べる } if (file_size < MIN_PACKET_SIZE){ CloseHandle(hFile); continue; // 小さすぎるファイルは検査しない } // 読み取りサイズは最大で SEARCH_SIZE len = SEARCH_SIZE; if (file_size < SEARCH_SIZE) len = (unsigned int)file_size; //printf("num = %d, file_size = %I64d\n", num, file_size); time_last = GetTickCount(); // 末尾から遡って読み込んでいく file_off = file_size; while (len > 0){ file_off -= len; // 読み取り開始位置になる // SEARCH_SIZE を読み込んでバッファーの先頭に入れる if (file_read_data(hFile, file_off, buf, len)){ len = 0; // 読み込み時にエラーが発生したファイルはそれ以上検査しない break; } // Main packet を探す off = 0; while (off < len){ if (match_magic(buf + off) == 0){ memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ if (packet_size != 0){ off += 8; continue; } memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (off + packet_size > SEARCH_SIZE * 3)){ off += 8; continue; } // パケット全体がバッファー内にあれば破損してないかを調べる data_md5(buf + (off + 32), (packet_size - 32), hash); if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 if (memcmp(buf + (off + 48), "PAR 2.0\0Main\0\0\0\0", 16) == 0){ // 念のためデータの整合性を検査する rv = 0; memcpy(&block_size, buf + (off + 68), 4); // Slice size の上位 4バイトを確かめる if (block_size != 0) // ブロック・サイズが 4GB以上だとエラー rv++; memcpy(&block_size, buf + (off + 64), 4); // Slice size if (block_size > MAX_BLOCK_SIZE) // 対応するブロック・サイズは 1GB まで rv++; if ((block_size & 3) != 0) // ブロック・サイズは 4 の倍数のはず rv++; memcpy(&entity_num, buf + (off + 72), 4); // Number of files in the recovery set. file_num = (packet_size - 76) / 16; if (file_num <= 0) rv++; if (entity_num > file_num) rv++; if (rv == 0){ memcpy(set_id, buf + (off + 32), 16); // Recovery Set ID memmove(buf, buf + (off + 76), packet_size - 76); // File ID をバッファーの先頭に移す CloseHandle(hFile); // Recovery Set の情報を表示する printf("Recovery Set ID\t\t: "); print_hash(set_id); printf("\n"); printf("Input File Slice size\t: %u\n", block_size); printf("Input File total count\t: %d\n", file_num); printf("Recovery Set file count : %d\n", entity_num); return 0; } } off += packet_size; // パケットの終端まで一気に飛ぶ } else { off += 8; } } else { off++; } } // 簡易検査なら検査領域を制限する(ファイルの後半分だけ) if (((switch_v & 1) != 0) && (file_off < file_size / 2)){ // 最低でも後半分は検査する len = 0; break; } // バッファーの前半 SEARCH_SIZE * 2 を次の読み込みサイズ分だけ後ろにずらす len = SEARCH_SIZE; if (file_off < SEARCH_SIZE) len = (unsigned int)file_off; if (len > 0) memmove(buf + len, buf, SEARCH_SIZE * 2); // 経過表示 if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((int)(((end_size + file_size - file_off) * 1000) / total_file_size))){ CloseHandle(hFile); return 2; } time_last = GetTickCount(); } } CloseHandle(hFile); end_size += file_size; } if ((prog_last >= 0) && (prog_last != 1000)) printf("100.0%%\r"); return 1; } // ファイル情報のパケットを探す int search_file_packet( char *ascii_buf, unsigned char *buf, // 作業バッファー wchar_t *par_comment, // Unicode コメントを入れる unsigned char *set_id, // Recovery Set ID を確かめる int flag_sanitize, // 0以外 = ファイル名を浄化する file_ctx_r *files) // 各ソース・ファイルの情報 { char par_client[COMMENT_LEN], ascii_comment[COMMENT_LEN]; unsigned char hash[16]; wchar_t uni_buf[MAX_LEN]; int i, j, k, recv_off, find_num; int len, off, max, used_CP = CP_UTF8; unsigned int packet_size, time_last; __int64 file_size, file_off, file_next, end_size; HANDLE hFile; par_client[0] = 0; par_comment[0] = 0; ascii_comment[0] = 0; list_len = 1; // 先頭に不明用の null 文字を置く list_max = ALLOC_LEN; list_buf = (wchar_t *)malloc(list_max * 2); if (list_buf == NULL){ printf("malloc, %d\n", list_max * 2); return 1; } list_buf[0] = 0; find_num = 0; recv_off = 0; end_size = 0; while (recv_off < recv_len){ if (cancel_progress() != 0) // キャンセル処理 return 2; //utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output); //printf("verifying %s\n", ascii_buf); // リカバリ・ファイルを開く hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす recv_off++; recv_off++; if (hFile == INVALID_HANDLE_VALUE) continue; // エラーが発生したら次のリカバリ・ファイルを調べる // ファイルのサイズを取得する if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ CloseHandle(hFile); continue; // エラーが発生したら次のリカバリ・ファイルを調べる } if (file_size < MIN_PACKET_SIZE){ CloseHandle(hFile); continue; // 小さすぎるファイルは検査しない } file_off = 0; time_last = GetTickCount(); // 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む off = 0; len = SEARCH_SIZE * 3; if (file_size < SEARCH_SIZE * 3) len = (unsigned int)file_size; // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる if (file_read_data(hFile, 0, buf, len)){ CloseHandle(hFile); continue; // エラーが発生したら次のリカバリ・ファイルを調べる } file_next = len; // 次の読み込み開始位置 max = len; if (len > SEARCH_SIZE) max = SEARCH_SIZE; // パケットを探す while (len > 0){ while (off < max){ if (match_magic(buf + off) == 0){ memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ if (packet_size != 0){ off += 8; continue; } memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){ off += 8; continue; } if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず off += 8; continue; } if ((switch_v & 1) == 0){ // 簡易検査でなければ // パケットが破損してないかを調べる if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){ off += 8; // 計算できなかったら } else { if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 // バッファー内のオフセットとパケット・サイズ分ずらす if (file_next < file_size) file_next = file_off + off + packet_size; off = max; // 一気に末尾までいく } else { off += 8; // 破損パケットなら } } } else { // 簡易検査では巨大パケットが完全かは調べない // バッファー内のオフセットとパケット・サイズ分ずらす if (file_next < file_size) file_next = file_off + off + packet_size; off = max; // 一気に末尾までいく } continue; } // パケットが破損してないかを調べる data_md5(buf + (off + 32), (packet_size - 32), hash); if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ off += packet_size; continue; } /* memset(ascii_buf, 0, 9); memcpy(ascii_buf, buf + (off + 56), 8); printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off); */ if ((memcmp(buf + (off + 48), "PAR 2.0\0FileDesc", 16) == 0) && (packet_size - 120 < MAX_LEN * 3)){ // File Description packet memcpy(ascii_buf, buf + (off + 120), packet_size - 120); ascii_buf[packet_size - 120] = 0; // 末尾を null 文字にしておく j = -1; // File ID が重複してないか確認する for (i = 0; i < file_num; i++){ if (memcmp(buf + (off + 64), files[i].id, 16) == 0){ // File ID でどのファイルか探す if (files[i].name < 0){ // 新しく発見したのなら break; } else { // 同じ情報が既に在った場合 j = i; } } } if (i < file_num){ memcpy(files[i].hash, buf + (off + 80), 32); memcpy(&(files[i].size), buf + (off + 112), 8); if ((j >= 0) && (i != j)){ // File ID が同じファイルが他に存在する // ファイル内容が異なるのに同じ File ID になると区別できないので駄目! // 逆に、ファイル内容 (ハッシュ値とサイズ) が同じなら問題ない? if ((files[i].size != files[j].size) || (memcmp(files[i].hash, files[j].hash, 32) != 0)){ printf("same File ID, %d and %d\n", j, i); return 1; } } // パケット内に記録されてるファイル名をユニコードに変換する if (used_CP != CP_UTF8){ // QuickPar などファイル名が UTF-8 以外なら // 変換できるか調べる j = cp_to_utf16(ascii_buf, uni_buf, used_CP); if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない j = cp_to_utf16(ascii_buf, uni_buf, 1252); if (j == 0){ k = convert_filename(1252, used_CP, files); // これまでのファイル名を変換しなおす if (k != 0){ printf("convert_filename\n"); return 1; } } used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす } } else if (check_utf8(ascii_buf) != 0){ // UTF-8 以外の文字をみつけたら used_CP = CP_ACP; // これ以降のファイル名は全て UTF-8 以外とみなす // ACP で変換できるか調べる j = cp_to_utf16(ascii_buf, uni_buf, used_CP); if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない j = cp_to_utf16(ascii_buf, uni_buf, 1252); used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす } if (j == 0){ k = convert_filename(used_CP, CP_UTF8, files); // これまでのファイル名を変換しなおす if (k == 1){ // CP1252 では変換エラーは発生しない j = cp_to_utf16(ascii_buf, uni_buf, 1252); k = convert_filename(1252, CP_UTF8, files); used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす } if (k != 0){ printf("convert_filename\n"); return 1; } } } else { utf8_to_utf16(ascii_buf, uni_buf); j = 0; } if (j == 0){ files[i].name = list_len; if (add_file_path(uni_buf)){ printf("add_file_path\n"); return 1; } } else { files[i].name = 0; // パケットは存在するけどファイル名の変換に失敗した } find_num++; // 完全な File Description packet の数 } } else if ((par_client[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0Creator\0", 16) == 0)){ // Creater packet i = packet_size - 64; // Creater のバイト数 if (i >= COMMENT_LEN) i = COMMENT_LEN - 1; memcpy(par_client, buf + (off + 64), i); par_client[i] = 0; // 末尾を null 文字にする } else if ((par_comment[0] == 0) && (ascii_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommASCI", 16) == 0)){ // ASCII Comment packet i = packet_size - 64; // コメントのバイト数 if (i >= COMMENT_LEN) i = COMMENT_LEN - 1; memcpy(ascii_comment, buf + (off + 64), i); ascii_comment[i] = 0; // 末尾を null 文字にする } else if ((par_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommUni\0", 16) == 0)){ // Unicode Comment packet i = packet_size - 80; // コメントのバイト数 if (i >= COMMENT_LEN * 2) i = COMMENT_LEN * 2 - 2; memcpy(par_comment, buf + (off + 80), i); par_comment[i / 2] = 0; // 末尾を null 文字にする } else if ((memcmp(buf + (off + 48), "PAR 2.0\0UniFileN", 16) == 0) && (packet_size - 80 < MAX_LEN * 2)){ // Unicode Filename packet memset(uni_buf, 0, sizeof(uni_buf)); memcpy(uni_buf, buf + (off + 80), packet_size - 80); for (i = 0; i < file_num; i++){ if (memcmp(buf + (off + 64), files[i].id, 16) == 0) // File ID が一致すれば break; } if (i < file_num){ // File Description packet を先に読み込んでる状態でしか認識しない if (files[i].name == 0){ // パケットは存在するけどファイル名の変換に失敗してる場合 // 新しいファイル名 files[i].name = list_len; if (add_file_path(uni_buf)){ printf("add_file_path\n"); return 1; } } else if ((files[i].name > 0) && (wcscmp(uni_buf, list_buf + files[i].name) != 0)){ // パケットが存在してファイル名も取得してるけどユニコード版とは異なる場合 k = (int)wcslen(list_buf + files[i].name); list_len = remove_file_path(list_buf, list_len, files[i].name); for (j = 0; j < file_num; j++){ if (files[j].name > files[i].name) files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく } // 新しいファイル名 files[i].name = list_len; if (add_file_path(uni_buf)){ printf("add_file_path\n"); return 1; } } } } off += packet_size; // パケットの終端まで一気に飛ぶ } else { off += 8; } } else { off++; } } // 全てのファイル情報を取得してしまえば抜ける if ((find_num >= file_num) && (par_client[0] != 0)) break; if (file_next < file_size){ // ファイル・データがまだ残ってれば if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら file_off = file_next; // 読み取りサイズは最大で SEARCH_SIZE * 3 off = 0; len = SEARCH_SIZE * 3; if (file_size < file_next + SEARCH_SIZE * 3) len = (unsigned int)(file_size - file_next); // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる if (file_read_data(hFile, file_next, buf, len)){ off = len; // 読み込み時にエラーが発生した部分は検査しない file_next = file_size; // 次は読み込まず、このファイルの検査を終える } file_next += len; // 次の読み込み開始位置 max = len; if (len > SEARCH_SIZE) max = SEARCH_SIZE; } else { // 次の SEARCH_SIZE を読み込む // バッファーの内容を前にずらす memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE); file_off += SEARCH_SIZE; // 読み取りサイズは最大で SEARCH_SIZE off -= max; len = SEARCH_SIZE; if (file_size < file_next + SEARCH_SIZE) len = (unsigned int)(file_size - file_next); // 最大 SEARCH_SIZE を読み込んでバッファーの末尾に入れる if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){ memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく file_next = file_size; // 次は読み込まず、このファイルの検査を終える } file_next += len; // 次の読み込み開始位置 max = SEARCH_SIZE; len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す } } else { // バッファー内の残りデータを処理する off -= max; len -= max; if (len > 0){ if (len > SEARCH_SIZE){ max = SEARCH_SIZE; memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE); } else { max = len; memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす } file_off += SEARCH_SIZE; } } // 経過表示 if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((int)(((end_size + file_off) * 1000) / total_file_size))){ CloseHandle(hFile); return 2; } time_last = GetTickCount(); } } CloseHandle(hFile); end_size += file_size; // 全てのファイル情報を取得してしまえば抜ける if ((find_num >= file_num) && (par_client[0] != 0)) break; } // QuickPar と phpar2 はファイル名を UTF-8 で記録しないことがわかってる if ((used_CP == CP_UTF8) && (par_client[0] != 0) && (non_utf8_client(par_client) != 0)){ k = convert_filename(CP_ACP, CP_UTF8, files); // これまでのファイル名を変換しなおす if (k == 1){ used_CP = 1252; // Latin-1 CP1252 で変換を試みる k = convert_filename(1252, CP_UTF8, files); } if (k != 0){ printf("convert_filename\n"); return 1; } } // 作成したクライアントとコメントを表示する if (par_client[0] != 0){ for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する if (par_client[i] == 0){ break; } else if ((par_client[i] <= 31) || (par_client[i] >= 127)){ // 制御文字を消す、非 ASCII 文字も par_client[i] = ' '; } } printf("Creator : %s\n", par_client); } if ((par_comment[0] == 0) && (ascii_comment[0] != 0)){ // ユニコードのコメントを優先する if (!MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, ascii_comment, -1, par_comment, COMMENT_LEN)) par_comment[0] = 0; } if (par_comment[0] != 0){ for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する if (par_comment[i] == 0){ break; } else if ((par_comment[i] <= 31) || (par_comment[i] == 127)){ // 制御文字を消す par_comment[i] = ' '; } } if (!utf16_to_cp(par_comment, ascii_buf, cp_output)) printf("Comment : %s\n", ascii_buf); } write_ini_file2(par_client, par_comment); // リスト内のファイル名を検査して、問題があれば浄化する // ついでにディレクトリ記号を「\」に統一する if (flag_sanitize != 0){ max = 0; for (i = 0; i < file_num; i++){ if (files[i].name <= 0) continue; // ファイル名が取得できなかったファイルはとばす wcscpy(uni_buf, list_buf + files[i].name); off = sanitize_filename(uni_buf, files, i); if (off != 0){ if ((off == 1) || (off == 9)){ // ディレクトリ記号を含んでる wcscpy(list_buf + files[i].name, uni_buf); off ^= 1; } if (off != 0){ if (max == 0){ max = 1; printf("\nWarning about filenames :\n"); } utf16_to_cp(uni_buf, ascii_buf, cp_output); if (off == 16){ max++; printf("file%d: \"%s\" is invalid\n", i, ascii_buf); } else if (off == 8){ printf("file%d: \"%s\" is invalid\n", i, ascii_buf); } else { printf("file%d: \"%s\" was sanitized\n", i, ascii_buf); if (off & 4){ // 文字数が変わった k = (int)wcslen(list_buf + files[i].name); list_len = remove_file_path(list_buf, list_len, files[i].name); for (j = 0; j < file_num; j++){ if (files[j].name > files[i].name) files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく } // 新しいファイル名 files[i].name = list_len; if (add_file_path(uni_buf)){ printf("add_file_path\n"); return 1; } } else { // 文字数が同じなら元の場所にコピーする wcscpy(list_buf + files[i].name, uni_buf); } } } } } if (max > 1) return 1; } return 0; } // 修復用のパケットを探す // 0~= 不完全なリカバリ・ファイルの数を返す、-2=Cansel int search_recovery_packet( char *ascii_buf, unsigned char *buf, // 作業バッファー wchar_t *file_path, unsigned char *set_id, // Recovery Set ID を確かめる HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル (verify なら NULL) file_ctx_r *files, // 各ソース・ファイルの情報 source_ctx_r *s_blk, // 各ソース・ブロックの情報 parity_ctx_r *p_blk) // 各パリティ・ブロックの情報 { unsigned char hash[16]; int i, j, recv_off, num, packet_count, bad_flag, file_block; int find_num, find_new, recovery_lost; int len, off, max; unsigned int packet_size, time_last, meta_data[7]; __int64 file_size, file_off, file_next; HANDLE hFile; printf("\nLoading PAR File :\n"); printf(" Packet Slice Status : Filename\n"); fflush(stdout); count_last = 0; recovery_lost = 0; // 不完全なリカバリ・ファイルの数 first_num = 0; // 初めて見つけたパリティ・ブロックの数 num = 0; recv_off = 0; while (recv_off < recv_len){ if (cancel_progress() != 0) // キャンセル処理 return -2; //utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output); //printf("verifying %s\n", ascii_buf); // リカバリ・ファイルを開く hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE){ i = -1; } else { // 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い i = check_ini_recovery(hFile, meta_data); } if (i < 0){ // エラーが発生したら次のリカバリ・ファイルを調べる while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす recv_off++; recv_off++; continue; } if (i == 0){ memcpy(&file_size, meta_data, 8); find_num = 0; find_new = 0; file_off = 0; prog_last = -1; time_last = GetTickCount(); // 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む bad_flag = 0; off = 0; len = SEARCH_SIZE * 3; if (file_size < SEARCH_SIZE * 3) len = (unsigned int)file_size; if (file_size < MIN_PACKET_SIZE){ // 小さすぎるファイルは検査しない bad_flag |= 1; len = 0; } // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる if (file_read_data(hFile, 0, buf, len)){ off = len; // 読み込み時にエラーが発生した部分は検査しない bad_flag |= 128; file_next = file_size; // 次は読み込まず、このファイルの検査を終える } else { file_next = len; // 次の読み込み開始位置 } max = len; if (len > SEARCH_SIZE) max = SEARCH_SIZE; //printf("file_size = %I64d, max = %d, off = %d, len = %d\n", file_size, max, off, len); // パケットを探す packet_count = 0; while (len > 0){ while (off < max){ if (match_magic(buf + off) == 0){ memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ if (packet_size != 0){ bad_flag |= 1; off += 8; continue; } memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){ bad_flag |= 1; off += 8; continue; } if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず off += 8; continue; } // パケットが破損してないかを調べる if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){ off += 8; // 計算できなかったら } else { if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 if (memcmp(buf + (off + 32), set_id, 16) == 0){ // Recovery Set ID が同じなら // 少なくともパケットの先頭 SEARCH_SIZE * 2 まではバッファー内に存在する // バッファーに収まらないパケットは Recovery Slice packet だけのはず if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){ // Recovery Slice packet memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号 j &= 0xFFFF; // 番号は 16-bit (65536以上は 0~65535 の繰り返し) if (j == 65535) // (0 と 65535 は同じ) j = 0; if (j < parity_num){ if (p_blk[j].exist == 0){ find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数 first_num++; p_blk[j].exist = 1; p_blk[j].file = num; p_blk[j].off = file_off + off + 68; } write_ini_recovery(j, file_off + off + 68); find_num++; // リカバリ・ファイル内のパリティ・ブロックの数 } } packet_count++; } else { bad_flag |= 2; // 別の Set ID が混じってる } // バッファー内のオフセットとパケット・サイズ分ずらす if (file_next < file_size) file_next = file_off + off + packet_size; off = max; // 一気に末尾までいく } else { off += 8; // 破損パケットなら } } continue; } // パケットが破損してないかを調べる data_md5(buf + (off + 32), (packet_size - 32), hash); if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ bad_flag |= 2; // 別の Set ID が混じってる off += packet_size; continue; } /* memset(ascii_buf, 0, 9); memcpy(ascii_buf, buf + (off + 56), 8); printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off); */ if (memcmp(buf + (off + 48), "PAR 2.0\0IFSC\0\0\0\0", 16) == 0){ // Input File Slice Checksum packet for (j = 0; j < entity_num; j++){ if (memcmp(buf + (off + 64), files[j].id, 16) == 0){ // File ID が一致すれば if (files[j].state & 0x80) // チェックサムを新しく発見したのなら break; } } file_block = files[j].b_num; // ソース・ブロックの数 if ((j < entity_num) && ((int)((packet_size - 80) / 20) == file_block)){ files[j].state = 0; // そのファイルのチェックサムを見つけた for (i = 0; i < file_block; i++){ memcpy(s_blk[files[j].b_off + i].hash, buf + (off + 80 + (20 * i)), 20); memcpy(&(s_blk[files[j].b_off + i].crc), buf + (off + 80 + (20 * i) + 16), 4); s_blk[files[j].b_off + i].crc ^= window_mask; // CRC の初期値と最終処理の 0xFFFFFFFF を取り除く } } } else if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){ // Recovery Slice packet memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号 j &= 0xFFFF; // 番号は 16-bit (65536以上は 0~65535 の繰り返し) if (j == 65535) // (0 と 65535 は同じ) j = 0; if ((packet_size == 64 + 4 + block_size) && (j < parity_num)){ if (p_blk[j].exist == 0){ find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数 first_num++; p_blk[j].exist = 1; p_blk[j].file = num; p_blk[j].off = file_off + off + 68; } write_ini_recovery(j, file_off + off + 68); find_num++; // リカバリ・ファイル内のパリティ・ブロックの数 } } packet_count++; off += packet_size; // パケットの終端まで一気に飛ぶ } else { bad_flag |= 1; off += 8; } } else { bad_flag |= 1; off++; } } if (file_next < file_size){ // ファイル・データがまだ残ってれば if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら file_off = file_next; // 読み取りサイズは最大で SEARCH_SIZE * 3 off = 0; len = SEARCH_SIZE * 3; if (file_size < file_next + SEARCH_SIZE * 3) len = (unsigned int)(file_size - file_next); // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる if (file_read_data(hFile, file_next, buf, len)){ off = len; // 読み込み時にエラーが発生した部分は検査しない bad_flag |= 128; file_next = file_size; // 次は読み込まず、このファイルの検査を終える } file_next += len; // 次の読み込み開始位置 max = len; if (len > SEARCH_SIZE) max = SEARCH_SIZE; } else { // 次の SEARCH_SIZE を読み込む // バッファーの内容を前にずらす memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE); file_off += SEARCH_SIZE; // 読み取りサイズは最大で SEARCH_SIZE off -= max; len = SEARCH_SIZE; if (file_size < file_next + SEARCH_SIZE) len = (unsigned int)(file_size - file_next); // 最大 SEARCH_SIZE を読み込んでバッファーの末尾 SEARCH_SIZE に入れる if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){ memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく bad_flag |= 128; file_next = file_size; // 次は読み込まず、このファイルの検査を終える } file_next += len; // 次の読み込み開始位置 max = SEARCH_SIZE; len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す } } else { // バッファー内の残りデータを処理する //printf("max = %d, off = %d, len = %d\n", max, off, len); off -= max; len -= max; if (len > 0){ if (len > SEARCH_SIZE){ max = SEARCH_SIZE; memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE); } else { max = len; memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす } file_off += SEARCH_SIZE; } } // 経過表示 if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress_file((int)((file_off * 1000) / file_size), first_num, recv_buf + recv_off)){ CloseHandle(hFile); write_ini_recovery2(-1, 0, 0, meta_data); return -2; } time_last = GetTickCount(); } } write_ini_recovery2(packet_count, find_num, bad_flag, meta_data); } else { // 検査済みなら記録を読み込む find_new = read_ini_recovery(num, &packet_count, &find_num, &bad_flag, p_blk); if (find_new < 0){ bad_flag = 1; packet_count = 0; find_num = 0; } else { first_num += find_new; // 新たに発見したパリティ・ブロックの数を合計する } } print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する if ((find_new > 0) && (rcv_hFile != NULL)){ rcv_hFile[num] = hFile; // 後から読み込めるように開いたままにする } else { CloseHandle(hFile); } // リカバリ・ファイルの名前 if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら utf16_to_cp(offset_file_name(recv_buf + recv_off), ascii_buf, cp_output); // ファイル名だけにする } else { path_to_cp(recv_buf + recv_off, ascii_buf, cp_output); // パスも表示する } if (bad_flag == 0){ // 良好 (パケット単位なので完全に元と同じかは判別できない) printf("%7d %5d Good : \"%s\"\n", packet_count, find_num, ascii_buf); // 削除する時用に記録しておく len = (int)wcslen(recv_buf + recv_off); wcscpy(recv2_buf + recv2_len, recv_buf + recv_off); recv2_len += len + 1; // 次のファイルの位置にずらす recv_off += len + 1; } else if (packet_count == 0){ // パケットが全く見つからなかった (リカバリ・ファイルではない) // 別の Set ID のリカバリ・ファイルも含む printf(" 0 0 Useless : \"%s\"\n", ascii_buf); recovery_lost++; if (bad_flag == 2){ // 別の Set ID のリカバリ・ファイルで良好なら // 次のファイルの位置にずらす while (recv_buf[recv_off] != 0) recv_off++; recv_off++; } else { // リカバリ・ファイルでなければリストから取り除く (ファイル数はそのまま) recv_len = remove_file_path(recv_buf, recv_len, recv_off); } } else { printf("%7d %5d Damaged : \"%s\"\n", packet_count, find_num, ascii_buf); recovery_lost++; // 削除する時用に記録しておく len = (int)wcslen(recv_buf + recv_off); wcscpy(recv2_buf + recv2_len, recv_buf + recv_off); recv2_len += len + 1; // 破損したリカバリ・ファイルはリストから取り除く (ファイル数はそのまま) recv_len = remove_file_path(recv_buf, recv_len, recv_off); } fflush(stdout); num++; // 次のファイルへ } /*{ FILE *fp; fp = fopen("par_list.txt", "wb"); fwrite(recv_buf, 2, recv_len, fp); fclose(fp); fp = fopen("par_list2.txt", "wb"); fwrite(recv2_buf, 2, recv2_len, fp); fclose(fp); }*/ json_file_list(files); // JSONファイルに記録する return recovery_lost; }