// create.c // Copyright : 2023-10-22 Yutaka Sawada // License : GPL #ifndef _UNICODE #define _UNICODE #endif #ifndef UNICODE #define UNICODE #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 // Windows Vista or later #endif #include #include #include #include "common2.h" #include "phmd5.h" #include "md5_crc.h" #include "version.h" #include "gf16.h" #include "create.h" //#define TIMER // 実験用 // ソート時に項目を比較する static int sort_cmp(const void *elem1, const void *elem2) { const file_ctx_c *file1, *file2; int i; file1 = elem1; file2 = elem2; // サイズが 0 の空ファイルやフォルダは non-recovery set に含める if (file1->size == 0){ if (file2->size > 0) return 1; } else { if (file2->size == 0) return -1; } // File ID の順に並び替える for (i = 15; i >= 0; i--){ if (file1->id[i] > file2->id[i]){ return 1; // 1 > 2 } else if (file1->id[i] < file2->id[i]){ return -1; // 1 < 2 } } return 0; // 1 = 2 } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // PAR 2.0 のパケット・ヘッダーを作成する void set_packet_header( unsigned char *buf, // ヘッダーを格納する 64バイトのバッファー unsigned char *set_id, // Recovery Set ID、16バイト int type_num, // そのパケットの型番号 unsigned int body_size) // パケットのデータ・サイズ { // Magic sequence memcpy(buf, "PAR2\0PKT", 8); // Length of the entire packet body_size += 64; // パケット全体のサイズにする memcpy(buf + 8, &body_size, 4); memset(buf + 12, 0, 4); // MD5 Hash of packet は後で計算する // Recovery Set ID memcpy(buf + 32, set_id, 16); // Type memcpy(buf + 48, "PAR 2.0\0", 8); memset(buf + 56, 0, 8); switch (type_num){ case 1: // Main packet memcpy(buf + 56, "Main", 4); break; case 2: // File Description packet memcpy(buf + 56, "FileDesc", 8); break; case 3: // Input File Slice Checksum packet memcpy(buf + 56, "IFSC", 4); break; case 4: // Recovery Slice packet memcpy(buf + 56, "RecvSlic", 8); break; case 5: // Creator packet memcpy(buf + 56, "Creator", 7); break; case 10: // Unicode Filename packet memcpy(buf + 56, "UniFileN", 8); break; case 11: // ASCII Comment packet memcpy(buf + 56, "CommASCI", 8); break; case 12: // Unicode Comment packet memcpy(buf + 56, "CommUni", 7); break; } } // ソース・ファイルの情報を集める int get_source_files(file_ctx_c *files) { unsigned char work_buf[16 + 8 + (MAX_LEN * 3)]; unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; wchar_t file_name[MAX_LEN], file_path[MAX_LEN]; int i, list_off = 0, num, len, max = 0; WIN32_FILE_ATTRIBUTE_DATA AttrData; wcscpy(file_path, base_dir); for (num = 0; num < file_num; num++){ files[num].name = list_off; // ファイル名の位置を記録する wcscpy(file_name, list_buf + list_off); // ファイル名が駄目だと警告する if (check_filename(file_name) != 0){ if (max == 0){ max = 1; printf("\nWarning about filenames :\n"); } utf16_to_cp(file_name, work_buf, cp_output); printf("file%d: \"%s\" is invalid\n", num, work_buf); } //list_off += (wcslen(file_name) + 1); while (list_buf[list_off] != 0) list_off++; list_off++; // 属性を調べる wcscpy(file_path + base_len, file_name); if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ print_win32_err(); printf_cp("GetFileAttributesEx, %s\n", file_name); return 1; } if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら memcpy(files[num].hash, hash0, 16); files[num].size = 0; } else { // ファイルなら files[num].size = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; if (files[num].size == 0){ // 空のファイルなら memcpy(files[num].hash, hash0, 16); } else { // ファイルの先頭 16KB 分のハッシュ値を計算する if (file_md5_16(file_path, files[num].hash)){ printf_cp("file_md5_16, %s\n", file_name); return 1; } } } // File ID を計算する memcpy(work_buf, files[num].hash, 16); memcpy(work_buf + 16, &(files[num].size), 8); unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする utf16_to_utf8(file_name, work_buf + 24); len = (int)strlen(work_buf + 24); // File ID 計算時のファイル名のサイズは 4の倍数にしない data_md5(work_buf, 24 + len, files[num].id); // 他のファイルと同じ File ID になってないか調べる do { for (i = 0; i < num; i++){ if (memcmp(files[num].id, files[i].id, 16) == 0){ // File ID が同じなら files[num].id[0] += 1; // 最下位バイトを変化させる break; } } } while (i < num); // File ID を必ずユニークな値にする } // ファイルを File ID の順に並び替える qsort(files, file_num, sizeof(file_ctx_c), sort_cmp); return 0; } // 共通パケットを作成する int set_common_packet( unsigned char *buf, int *packet_num, // 共通パケットの数 int switch_u, // ユニコードのファイル名も記録する file_ctx_c *files) { char ascii_buf[MAX_LEN * 3]; unsigned char set_id[16], file_hash[16], *main_packet_buf; unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; wchar_t file_name[MAX_LEN]; int i, err, off, off2, num, len, data_size, main_packet_size; unsigned int time_last; __int64 prog_now = 0; #ifdef TIMER unsigned int time_start = GetTickCount(); #endif print_progress_text(0, "Computing file hash"); // 最初に Main packet を作成しておく main_packet_size = 12 + (file_num * 16); main_packet_buf = (unsigned char *)malloc(main_packet_size); if (main_packet_buf == NULL){ printf("malloc, %d\n", main_packet_size); return 1; } memcpy(main_packet_buf, &block_size, 4); memset(main_packet_buf + 4, 0, 4); memcpy(main_packet_buf + 8, &entity_num, 4); for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する *packet_num = 1; // ファイルごとのパケットを作成する time_last = GetTickCount(); off = 0; for (num = 0; num < file_num; num++){ // File Description packet off2 = off; // 位置を記録しておく memcpy(buf + (off + 64), files[num].id, 16); memcpy(buf + (off + 64 + 32), files[num].hash, 16); // 全体のハッシュ値は後で書き込む memcpy(buf + (off + 64 + 48), &(files[num].size), 8); // ファイル名を UTF-8 に変換する wcscpy(file_name, list_buf + files[num].name); unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする utf16_to_utf8(file_name, ascii_buf); len = (int)strlen(ascii_buf); for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) ascii_buf[i] = 0; len = (len + 3) & 0xFFFFFFFC; memcpy(buf + (off + 64 + 56), ascii_buf, len); data_size = 56 + len; set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; if (files[num].size > 0){ // Input File Slice Checksum packet // QuickPar はなぜかサイズが 0 のファイルに対してもチェックサムのパケットが必要・・・ // 無意味なパケットを避けるために non-recovery set を使った方がいい? memcpy(buf + (off + 64), files[num].id, 16); // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する err = file_hash_crc(list_buf + files[num].name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); if (err){ if (err == 1) printf_cp("file_hash_crc, %s\n", file_name); off = err; goto error_end; } len = (int)((files[num].size + (__int64)block_size - 1) / block_size); // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する memset(files[num].hash, 0, 16); for (i = 0; i < len; i++){ // XOR して 16バイトに減らす memcpy(&err, buf + (off + 64 + 16 + 20 * i + 16), 4); ((unsigned int *)files[num].hash)[i & 3] ^= err; } // printf("%d: 0x%08X %08X %08X %08X\n", num, // ((unsigned int *)files[num].hash)[0], ((unsigned int *)files[num].hash)[1], // ((unsigned int *)files[num].hash)[2], ((unsigned int *)files[num].hash)[3]); i = 16 + 20 * len; // パケット内容のサイズ set_packet_header(buf + off, set_id, 3, i); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + i, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + i); (*packet_num)++; } else { memcpy(file_hash, hash0, 16); } // File Description packet の続き memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む data_md5(buf + (off2 + 32), 32 + data_size, buf + (off2 + 16)); // パケットの MD5 を計算する if (switch_u != 0){ // ユニコードのファイル名 // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない len = (int)wcslen(file_name); for (i = 0; i < len; i++){ if (file_name[i] != ascii_buf[i]) break; } if (i < len){ // Unicode Filename packet memcpy(buf + (off + 64), files[num].id, 16); len = (int)wcslen(file_name) * 2; memcpy(buf + (off + 64 + 16), file_name, len); if (len & 3) memset(buf + (off + 64 + 16 + len), 0, 2); data_size = 16 + ((len + 3) & 0xFFFFFFFC); set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; } } } print_progress_done(); // 改行して行の先頭に戻しておく // Main packet set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する memcpy(buf + (off + 64), main_packet_buf, main_packet_size); data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + main_packet_size); #ifdef TIMER time_start = GetTickCount() - time_start; printf("hash %d.%03d sec", time_start / 1000, time_start % 1000); if (time_start > 0){ time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); printf(", %d MB/s\n", time_start); } else { printf("\n"); } #endif error_end: free(main_packet_buf); return off; } #define MAX_MULTI_READ 6 // SSDで同時に読み込む最大ファイル数 // SSD 上で複数ファイルのハッシュ値を同時に求めるバージョン int set_common_packet_multi( unsigned char *buf, int *packet_num, // 共通パケットの数 int switch_u, // ユニコードのファイル名も記録する file_ctx_c *files) { char ascii_buf[MAX_LEN * 3]; unsigned char set_id[16], file_hash[16], *main_packet_buf; unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; wchar_t file_name[MAX_LEN]; int i, err = 0, off, off2, num, len, data_size, main_packet_size, multi_read; unsigned int rv, time_last; __int64 prog_now = 0, prog_loop; HANDLE hSub[MAX_MULTI_READ]; FILE_HASH_TH th[MAX_MULTI_READ]; #ifdef TIMER unsigned int time_start = GetTickCount(); #endif memset(hSub, 0, sizeof(HANDLE) * MAX_MULTI_READ); memset(th, 0, sizeof(FILE_HASH_TH) * MAX_MULTI_READ); // 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_MULTI_READ) multi_read = MAX_MULTI_READ; } else { // SATA SSD multi_read = 2; } if (multi_read > entity_num) multi_read = entity_num; #ifdef TIMER printf("cpu_num = %d, entity_num = %d, multi_read = %d\n", cpu_num, entity_num, multi_read); #endif print_progress_text(0, "Computing file hash"); // 最初に Main packet を作成しておく main_packet_size = 12 + (file_num * 16); main_packet_buf = (unsigned char *)malloc(main_packet_size); if (main_packet_buf == NULL){ printf("malloc, %d\n", main_packet_size); return 1; } memcpy(main_packet_buf, &block_size, 4); memset(main_packet_buf + 4, 0, 4); memcpy(main_packet_buf + 8, &entity_num, 4); for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する *packet_num = 1; // ファイルごとのパケットを作成する time_last = GetTickCount(); off = 0; for (num = 0; num < file_num; num++){ // File Description packet off2 = off; // 位置を記録しておく memcpy(buf + (off + 64), files[num].id, 16); memcpy(buf + (off + 64 + 32), files[num].hash, 16); // 全体のハッシュ値は後で書き込む memcpy(buf + (off + 64 + 48), &(files[num].size), 8); // ファイル名を UTF-8 に変換する wcscpy(file_name, list_buf + files[num].name); unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする utf16_to_utf8(file_name, ascii_buf); len = (int)strlen(ascii_buf); for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) ascii_buf[i] = 0; len = (len + 3) & 0xFFFFFFFC; memcpy(buf + (off + 64 + 56), ascii_buf, len); data_size = 56 + len; // data_size = off - off2 - 64 なのでサイズを記録しなくても逆算できる set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; if (files[num].size > 0){ // Input File Slice Checksum packet // QuickPar はなぜかサイズが 0 のファイルに対してもチェックサムのパケットが必要・・・ // 無意味なパケットを避けるために non-recovery set を使った方がいい? memcpy(buf + (off + 64), files[num].id, 16); len = (int)((files[num].size + (__int64)block_size - 1) / block_size); // ブロック数 data_size = 16 + 20 * len; // パケット内容のサイズ set_packet_header(buf + off, set_id, 3, data_size); // パケット・ヘッダーを作成する if (multi_read < 2){ // ファイルを一個ずつ処理する // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する err = file_hash_crc(file_name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); if (err){ if (err == 1) printf_cp("file_hash_crc, %s\n", file_name); goto error_end; } // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する memset(files[num].hash, 0, 16); for (i = 0; i < len; i++){ // XOR して 16バイトに減らす memcpy(&rv, buf + (off + 64 + 16 + 20 * i + 16), 4); ((unsigned int *)files[num].hash)[i & 3] ^= rv; } // printf("%d: 0x%08X %08X %08X %08X\n", num, // ((unsigned int *)files[num].hash)[0], ((unsigned int *)files[num].hash)[1], // ((unsigned int *)files[num].hash)[2], ((unsigned int *)files[num].hash)[3]); data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する // File Description packet の続き memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む data_md5(buf + (off2 + 32), 32 + (off - off2 - 64), buf + (off2 + 16)); // パケットの MD5 を計算する } else { // 複数ファイルを同時に処理する if (num < multi_read){ // 最初の何個かは順番にスレッドを割り当てる i = num; } else { // 全て使用済みなら、どれかが終わるのを待つ do { rv = WaitForMultipleObjects(multi_read, hSub, FALSE, UPDATE_TIME / 2); if (GetTickCount() - time_last >= UPDATE_TIME){ prog_loop = 0; for (i = 0; i < multi_read; i++) prog_loop += th[i].loop; // サブ・スレッドの進捗状況を合計する prog_loop *= IO_SIZE; prog_loop += prog_now; if (print_progress((int)((prog_loop * 1000) / total_file_size))){ err = 2; goto error_end; } time_last = GetTickCount(); } } while (rv == WAIT_TIMEOUT); if (rv == WAIT_FAILED){ print_win32_err(); err = 1; goto error_end; } i = rv - WAIT_OBJECT_0; // 終了したスレッド if (GetExitCodeThread(hSub[i], &rv) == 0){ // サブ・スレッドのエラー確認 print_win32_err(); err = 1; goto error_end; } else if (rv != 0){ err = rv; goto error_end; } CloseHandle(hSub[i]); prog_now += th[i].file_size; } //printf("thread = %d, size = %I64d\n", i, files[num].size); th[i].file_name = list_buf + files[num].name; th[i].file_size = files[num].size; th[i].hash = buf + (off2 + 64 + 16); th[i].sum = buf + (off + 64 + 16); th[i].crc = (unsigned int *)(files[num].hash); th[i].loop = 0; //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する hSub[i] = (HANDLE)_beginthreadex(NULL, STACK_SIZE + MAX_LEN * 2, file_hash_crc2, (LPVOID)&(th[i]), 0, NULL); if (hSub[i] == NULL){ print_win32_err(); printf("error, sub-thread\n"); err = 1; goto error_end; } } off += (64 + data_size); (*packet_num)++; } else { // File Description packet の続き memcpy(buf + (off2 + 64 + 16), hash0, 16); // 予め計算してあるハッシュ値をコピーする data_md5(buf + (off2 + 32), 32 + (off - off2 - 64), buf + (off2 + 16)); // パケットの MD5 を計算する } if (switch_u != 0){ // ユニコードのファイル名 // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない len = (int)wcslen(file_name); for (i = 0; i < len; i++){ if (file_name[i] != ascii_buf[i]) break; } if (i < len){ // Unicode Filename packet memcpy(buf + (off + 64), files[num].id, 16); len = (int)wcslen(file_name) * 2; memcpy(buf + (off + 64 + 16), file_name, len); if (len & 3) memset(buf + (off + 64 + 16 + len), 0, 2); data_size = 16 + ((len + 3) & 0xFFFFFFFC); set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; } } } // Main packet set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する memcpy(buf + (off + 64), main_packet_buf, main_packet_size); data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + main_packet_size); if (multi_read >= 2){ // 全てのサブ・スレッドが終了するまで待つ do { rv = WaitForMultipleObjects(multi_read, hSub, TRUE, UPDATE_TIME / 2); if (GetTickCount() - time_last >= UPDATE_TIME){ prog_loop = 0; for (i = 0; i < multi_read; i++) prog_loop += th[i].loop; // サブ・スレッドの進捗状況を合計する prog_loop *= IO_SIZE; prog_loop += prog_now; if (print_progress((int)((prog_loop * 1000) / total_file_size))){ err = 2; goto error_end; } time_last = GetTickCount(); } } while (rv == WAIT_TIMEOUT); if (rv == WAIT_FAILED){ print_win32_err(); err = 1; goto error_end; } } print_progress_done(); // 改行して行の先頭に戻しておく #ifdef TIMER time_start = GetTickCount() - time_start; printf("hash %d.%03d sec", time_start / 1000, time_start % 1000); if (time_start > 0){ time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); printf(", %d MB/s\n", time_start); } else { printf("\n"); } #endif error_end: free(main_packet_buf); if (multi_read >= 2){ if (err != 0){ // エラーが発生した場合 for (i = 0; i < multi_read; i++) // 終了指示をだす InterlockedExchange(&(th[i].loop), -1); } for (i = 0; i < multi_read; i++){ if (hSub[i]){ WaitForSingleObject(hSub[i], INFINITE); GetExitCodeThread(hSub[i], &rv); if ((err == 0) && (rv != 0)) err = rv; CloseHandle(hSub[i]); } } } if (err) return err; return off; } // ハッシュ値を後で計算する int set_common_packet_1pass( unsigned char *buf, int *packet_num, // 共通パケットの数 int switch_u, // ユニコードのファイル名も記録する file_ctx_c *files) { char ascii_buf[MAX_LEN * 3]; unsigned char set_id[16], *main_packet_buf; unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; wchar_t file_name[MAX_LEN], file_path[MAX_LEN]; int i, off, num, len, data_size, main_packet_size; unsigned int time_last; __int64 prog_now = 0; wcscpy(file_path, base_dir); // 最初に Main packet を作成しておく main_packet_size = 12 + (file_num * 16); main_packet_buf = (unsigned char *)malloc(main_packet_size); if (main_packet_buf == NULL){ printf("malloc, %d\n", main_packet_size); return 1; } memcpy(main_packet_buf, &block_size, 4); memset(main_packet_buf + 4, 0, 4); memcpy(main_packet_buf + 8, &entity_num, 4); for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する *packet_num = 1; // ファイルごとのパケットを作成する time_last = GetTickCount(); off = 0; for (num = 0; num < file_num; num++){ // File Description packet memcpy(buf + (off + 64), files[num].id, 16); memcpy(buf + (off + 64 + 32), files[num].hash, 16); memcpy(buf + (off + 64 + 48), &(files[num].size), 8); // ファイル名を UTF-8 に変換する wcscpy(file_name, list_buf + files[num].name); wcscpy(file_path + base_len, file_name); unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする utf16_to_utf8(file_name, ascii_buf); len = (int)strlen(ascii_buf); for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) ascii_buf[i] = 0; len = (len + 3) & 0xFFFFFFFC; memcpy(buf + (off + 64 + 56), ascii_buf, len); data_size = 56 + len; set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する if (files[num].size > 0){ // ファイルの MD5-16k はもう不要なので、パケットの位置とサイズに変更する //printf("File[%d], off = %d, size = %d\n", num, off, data_size); memcpy(files[num].hash, &off, 4); // ハッシュ値の位置 = off + 64 + 16 memcpy(files[num].hash + 4, &data_size, 4); } else { memcpy(buf + (off + 64 + 16), hash0, 16); data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する } off += (64 + data_size); (*packet_num)++; if (files[num].size > 0){ // Input File Slice Checksum packet memcpy(buf + (off + 64), files[num].id, 16); len = (int)((files[num].size + (__int64)block_size - 1) / block_size); data_size = 16 + 20 * len; // パケット内容のサイズ set_packet_header(buf + off, set_id, 3, data_size); // パケット・ヘッダーを作成する // ファイルの MD5-16k はもう不要なので、パケットの位置とサイズに変更する //printf("Checksum[%d], off = %d, size = %d\n", num, off, data_size); memcpy(files[num].hash + 8, &off, 4); // チェックサムの位置 = off + 64 + 16 memcpy(files[num].hash + 12, &data_size, 4); //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; } if (switch_u != 0){ // ユニコードのファイル名 // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない len = (int)wcslen(file_name); for (i = 0; i < len; i++){ if (file_name[i] != ascii_buf[i]) break; } if (i < len){ // Unicode Filename packet memcpy(buf + (off + 64), files[num].id, 16); len = (int)wcslen(file_name) * 2; memcpy(buf + (off + 64 + 16), file_name, len); if (len & 3) memset(buf + (off + 64 + 16 + len), 0, 2); data_size = 16 + ((len + 3) & 0xFFFFFFFC); set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); (*packet_num)++; } } } // Main packet set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する memcpy(buf + (off + 64), main_packet_buf, main_packet_size); data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + main_packet_size); free(main_packet_buf); return off; } // パケットは作成済みなので、ハッシュ値だけ計算して追加する int set_common_packet_hash( unsigned char *buf, file_ctx_c *files) { unsigned char file_hash[16]; int err, off, off2, num, data_size; unsigned int time_last; __int64 prog_now = 0; #ifdef TIMER unsigned int time_start = GetTickCount(); #endif print_progress_text(0, "Computing file hash"); // ファイルごとのパケットを作成する time_last = GetTickCount(); for (num = 0; num < file_num; num++){ if (files[num].size > 0){ // Input File Slice Checksum packet memcpy(&off, files[num].hash + 8, 4); // チェックサムの位置 = off + 64 + 16 memcpy(&data_size, files[num].hash + 12, 4); //printf("off = %d, data_size = %d\n", off, data_size); // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する err = file_hash_crc(list_buf + files[num].name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); if (err){ if (err == 1) printf_cp("file_hash_crc, %s\n", list_buf + files[num].name); return err; } data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する // File Description packet memcpy(&off2, files[num].hash, 4); // ハッシュ値の位置 = off + 64 + 16 memcpy(&data_size, files[num].hash + 4, 4); //printf("off2 = %d, data_size = %d\n", off2, data_size); memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む data_md5(buf + (off2 + 32), 32 + data_size, buf + (off2 + 16)); // パケットの MD5 を計算する data_size = (int)((files[num].size + (__int64)block_size - 1) / block_size); // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する memset(files[num].hash, 0, 16); for (off2 = 0; off2 < data_size; off2++){ // XOR して 16バイトに減らす memcpy(&err, buf + (off + 64 + 16 + 20 * off2 + 16), 4); ((unsigned int *)files[num].hash)[off2 & 3] ^= err; } } } print_progress_done(); // 改行して行の先頭に戻しておく #ifdef TIMER time_start = GetTickCount() - time_start; printf("hash %d.%03d sec\n", time_start / 1000, time_start % 1000); #endif return 0; } // ソース・ファイルの情報から共通パケットのサイズを計算する int measure_common_packet( int *packet_num, // 共通パケットの数 int switch_u) // ユニコードのファイル名も記録する { char ascii_buf[MAX_LEN * 3]; wchar_t *file_name, file_path[MAX_LEN]; int i, list_off = 0, off = 0, len, data_size; __int64 file_size; WIN32_FILE_ATTRIBUTE_DATA AttrData; wcscpy(file_path, base_dir); // 最初に Main packet を作成しておく data_size = 12 + (file_num * 16); off += (64 + data_size); *packet_num = 1; // Item index for debug output if (split_size >= 4){ printf("\n Size Slice Split : Filename\n"); } else { printf("\n Size Slice : Filename\n"); } // ファイルごとのパケットを作成する while (list_off < list_len){ file_name = list_buf + list_off; //list_off += (wcslen(file_name) + 1); while (list_buf[list_off] != 0) list_off++; list_off++; // 属性を調べる wcscpy(file_path + base_len, file_name); if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ print_win32_err(); printf_cp("GetFileAttributesEx, %s\n", file_name); return 1; } if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら file_size = 0; } else { // ファイルなら file_size = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; } // Line for debug output i = 0; if (file_size > 0) i = (int)((file_size + block_size - 1) / block_size); // ブロック数 printf("%13I64d %6d", file_size, i); if (split_size >= 4){ i = (int)((file_size + split_size - 1) / split_size); // 分割数 printf(" %5d", i); } printf_cp(" : \"%s\"\n", file_name); // File Description packet // ファイル名を UTF-8 に変換する utf16_to_utf8(file_name, ascii_buf); len = (int)strlen(ascii_buf); len = (len + 3) & 0xFFFFFFFC; data_size = 56 + len; off += (64 + data_size); (*packet_num)++; if (file_size > 0){ // Input File Slice Checksum packet data_size = 16; data_size += (int)(((file_size + block_size - 1) / block_size) * 20); off += (64 + data_size); (*packet_num)++; } if (switch_u != 0){ // ユニコードのファイル名 // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない len = (int)wcslen(file_name); for (i = 0; i < len; i++){ if (file_name[i] != ascii_buf[i]){ break; } } if (i < len){ // Unicode Filename packet len = (int)wcslen(file_name) * 2; data_size = 16 + ((len + 3) & 0xFFFFFFFC); off += (64 + data_size); (*packet_num)++; } } } return off; } // 末尾パケットを作成する int set_footer_packet( unsigned char *buf, wchar_t *par_comment, // コメント unsigned char *set_id) // Recovery Set ID { unsigned char ascii_buf[COMMENT_LEN * 3]; int off = 0, len, data_size; if (par_comment[0] != 0){ // ASCII と Unicode のどちらか一方だけを記録する data_size = 0; // 7-bit ASCII 文字以外が存在するかどうかを調べる len = 0; while (par_comment[len] != 0){ if ((par_comment[len] & 0xFF80) != 0){ data_size = 3; break; } len++; } if (data_size == 0){ // ASCII への変換に失敗した場合はユニコード・パケットを記録する if (!WideCharToMultiByte(CP_ACP, 0, par_comment, -1, ascii_buf, COMMENT_LEN * 3, NULL, &len) || len){ data_size = 1; } else if (wcslen(par_comment) != strlen(ascii_buf)){ // 文字数とバイト数が一致しないなら data_size = 2; } } if (data_size == 0){ // ASCII Comment packet len = (int)strlen(ascii_buf); memcpy(buf + (off + 64), ascii_buf, len); if (len & 3) memset(buf + (off + 64 + len), 0, 3); data_size = (len + 3) & 0xFFFFFFFC; set_packet_header(buf + off, set_id, 11, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); } else { // Unicode Comment packet memset(buf + (off + 64), 0, 16); len = (int)wcslen(par_comment) * 2; memcpy(buf + (off + 64 + 16), par_comment, len); if (len & 3) memset(buf + (off + 64 + 16 + len), 0, 2); data_size = 16 + ((len + 3) & 0xFFFFFFFC); set_packet_header(buf + off, set_id, 12, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); } } // Creator packet strcpy(ascii_buf, "par2j v" PRODUCT_VERSION); len = (int)strlen(ascii_buf); memcpy(buf + (off + 64), ascii_buf, len); if (len & 3) memset(buf + (off + 64 + len), 0, 3); data_size = (len + 3) & 0xFFFFFFFC; //strcpy(buf + (off + 64), "par2j v1.1.6\0"); //data_size = 12; set_packet_header(buf + off, set_id, 5, data_size); // パケット・ヘッダーを作成する data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する off += (64 + data_size); return off; } // 末尾パケットのサイズを計算する int measure_footer_packet( wchar_t *par_comment) // コメント { unsigned char ascii_buf[COMMENT_LEN * 3]; int off = 0, len, data_size; if (par_comment[0] != 0){ // ASCII と Unicode のどちらか一方だけを記録する data_size = 0; // ASCII への変換に失敗した場合はユニコード・パケットを記録する if (!WideCharToMultiByte(CP_ACP, 0, par_comment, -1, ascii_buf, COMMENT_LEN * 3, NULL, &len) || len){ data_size = 1; } else if (wcslen(par_comment) != strlen(ascii_buf)){ // 文字数とバイト数が一致しないなら data_size = 2; } if (data_size == 0){ // ASCII Comment packet len = (int)strlen(ascii_buf); data_size = (len + 3) & 0xFFFFFFFC; off += (64 + data_size); } else { // Unicode Comment packet len = (int)wcslen(par_comment) * 2; data_size = 16 + ((len + 3) & 0xFFFFFFFC); off += (64 + data_size); } } // Creator packet //len = strlen("par2j v*.*.*\0"); //data_size = (len + 3) & 0xFFFFFFFC; off += (64 + 12); return off; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // ボリューム番号の桁数を求める static int calc_max_num( int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) int *max_num2) // 最大スライス個数の桁数 { int i, j, num; int exp_num, block_start, block_count, block_start_max, block_count_max = 0; if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... i = recovery_num; for (j = 1; j < 10; j++){ i /= 10; if (i == 0) break; } return j; // ファイル数の桁数 } block_start = 0; block_count = 0; exp_num = 1; for (num = 0; num < recovery_num; num++){ // リカバリ・ファイルのファイル名 switch (block_distri & 3){ case 0: // 同じ数なら block_count = parity_num / recovery_num; j = parity_num % recovery_num; // 割り切れない場合は if ((j > 0) && (num < j)) block_count++; break; case 1: // 倍々で異なる数にする if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる block_count = parity_num - block_start; } else { exp_num = recovery_limit & 0xFFFF; if (num >= 16){ block_count = exp_num; } else { block_count = ((unsigned int)recovery_limit >> 16) << num; if (block_count > exp_num) block_count = exp_num; } } break; case 2: // 1,2,4,8,16 と2の乗数なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { exp_num *= 2; } break; case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { switch (num % 4){ case 1: case 3: exp_num = exp_num * 2; break; case 2: exp_num = (exp_num / 2) * 5; break; } } break; } if (block_start + block_count > parity_num) block_count = parity_num - block_start; if (block_count_max < block_count) block_count_max = block_count; block_start += block_count; } block_start_max = first_num + block_start - block_count; // 最後に足した分を引く for (j = 1; j < 10; j++){ block_start_max /= 10; if (block_start_max == 0) break; } block_start_max = j; for (j = 1; j < 10; j++){ block_count_max /= 10; if (block_count_max == 0) break; } *max_num2 = j; return block_start_max; } // ランダムに書き込むには管理者権限が必要 //#define RANDOM_ACCESS // リカバリ・ファイルを作成して共通パケットをコピーする int create_recovery_file( wchar_t *recovery_path, // 作業用 int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) int packet_num, // 共通パケットの数 unsigned char *common_buf, // 共通パケットのバッファー int common_size, // 共通パケットのバッファー・サイズ unsigned char *footer_buf, // 末尾パケットのバッファー int footer_size, // 末尾パケットのバッファー・サイズ HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル parity_ctx_c *p_blk) // 各パリティ・ブロックの情報 { wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; int i, j, num; int exp_num, block_start, block_count, block_start_max, block_count_max; int repeat_max, packet_to, packet_from, packet_size, common_off; unsigned int time_last, prog_num = 0; __int64 file_off; #ifdef RANDOM_ACCESS HANDLE hToken; #endif #ifdef TIMER unsigned int time_start = GetTickCount(); #endif print_progress_text(0, "Constructing recovery file"); time_last = GetTickCount(); #ifdef RANDOM_ACCESS // SE_MANAGE_VOLUME_NAME 権限を有効にする if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) != 0){ LUID luid; if (LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &luid ) != 0){ TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL); if (GetLastError() != ERROR_SUCCESS){ print_win32_err(); } else { printf("\nSE_MANAGE_VOLUME_NAME: ok\n"); } } CloseHandle(hToken); } #endif // リカバリ・ファイルの拡張子には指定されたものを使う file_ext[0] = 0; wcscpy(recovery_base, recovery_file); tmp_p = offset_file_name(recovery_base); tmp_p = wcsrchr(tmp_p, '.'); if (tmp_p != NULL){ if (wcslen(tmp_p) < EXT_LEN){ wcscpy(file_ext, tmp_p); // 拡張子を記録しておく *tmp_p = 0; // 拡張子を取り除く } } // ボリューム番号の桁数を求める block_start_max = calc_max_num(block_distri, &block_count_max); // リカバリ・ファイルを作成して共通パケットを書き込む block_start = 0; block_count = 0; exp_num = 1; for (num = 0; num < recovery_num; num++){ // リカバリ・ファイルのファイル名 switch (block_distri & 3){ case 0: // 同じ数なら block_count = parity_num / recovery_num; j = parity_num % recovery_num; // 割り切れない場合は if ((j > 0) && (num < j)) block_count++; break; case 1: // 倍々で異なる数にする if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる block_count = parity_num - block_start; } else { exp_num = recovery_limit & 0xFFFF; if (num >= 16){ block_count = exp_num; } else { block_count = ((unsigned int)recovery_limit >> 16) << num; if (block_count > exp_num) block_count = exp_num; } } break; case 2: // 1,2,4,8,16 と2の乗数なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { exp_num *= 2; } break; case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { switch (num % 4){ case 1: case 3: exp_num = exp_num * 2; break; case 2: exp_num = (exp_num / 2) * 5; break; } } break; } if (block_start + block_count > parity_num) block_count = parity_num - block_start; if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); } else { // QuickPar方式、volXX+YY swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, first_num + block_start, block_count_max, block_count, file_ext); } // リカバリ・ファイルを開く //move_away_file(recovery_path); // 既存のファイルをどかす if (split_size == 1){ // 書庫ファイルに連結する場合は、後で読めるようにする rcv_hFile[num] = CreateFile(recovery_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } else { // 書庫ファイルに連結しないので、読み込む必要が無い rcv_hFile[num] = CreateFile(recovery_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } if (rcv_hFile[num] == INVALID_HANDLE_VALUE){ print_win32_err(); rcv_hFile[num] = NULL; printf_cp("cannot create file, %s\n", recovery_path); return 1; } // パケットの繰り返し回数を計算する repeat_max = 1; for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) repeat_max++; if ((packet_limit > 0) && (repeat_max > packet_limit)) repeat_max = packet_limit; // 繰り返し回数を制限する // リカバリ・ファイルの大きさを計算する file_off = (__int64)(68 + block_size) * block_count; file_off += (common_size * repeat_max) + footer_size; // そのサイズにする if (!SetFilePointerEx(rcv_hFile[num], *((PLARGE_INTEGER)&file_off), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } if (!SetEndOfFile(rcv_hFile[num])){ print_win32_err(); return 1; } #ifdef RANDOM_ACCESS if (!SetFileValidData(rcv_hFile[num], file_off)){ print_win32_err(); return 1; } #endif file_off = 0; repeat_max *= packet_num; // リカバリ・ファイルの共通パケットの数 packet_from = 0; common_off = 0; // Recovery Slice packet は後から書き込む for (j = block_start; j < block_start + block_count; j++){ prog_num++; // 経過表示 if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((prog_num * 1000) / parity_num)) return 2; time_last = GetTickCount(); } // Recovery Slice packet file_off += 68; p_blk[j].file = num; p_blk[j].off = file_off; // 開始位置だけ記録しておく file_off += block_size; // どれだけの共通パケットを書き込むか packet_size = 0; packet_to = (int)((__int64)repeat_max * (j - block_start + 1) / block_count); while (packet_to - packet_from > 0){ memcpy(&i, common_buf + (common_off + (packet_size + 8)), 4); // そのパケットのデータ・サイズを調べる packet_size += i; packet_from++; } if (packet_size > 0){ // 共通パケットを書き込む if (file_write_data(rcv_hFile[num], file_off, common_buf + common_off, packet_size)){ printf_cp("file_write_data, %s\n", recovery_path); return 1; } // オフセットが半分を超えたら戻しておく common_off += packet_size; if (common_off >= common_size) common_off -= common_size; file_off += packet_size; } } block_start += block_count; // 末尾パケットを書き込む if (file_write_data(rcv_hFile[num], file_off, footer_buf, footer_size)){ printf_cp("file_write_data, %s\n", recovery_path); return 1; } } print_progress_done(); // 改行して行の先頭に戻しておく #ifdef TIMER time_start = GetTickCount() - time_start; printf("write %d.%03d sec\n", time_start / 1000, time_start % 1000); #endif return 0; } // パリティ・ブロックが完成した後に、encode_method5 から直接呼び出す // リカバリ・ファイルを一個ずつ書き込んだ方が単純でいい! // 各リカバリ・ファイルのハンドル、各パリティ・ブロックの情報、は不要になるし int create_recovery_file_1pass( wchar_t *recovery_base, wchar_t *recovery_path, // 作業用 int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) int packet_num, // 共通パケットの数 unsigned char *common_buf, // 共通パケットのバッファー int common_size, // 共通パケットのバッファー・サイズ unsigned char *footer_buf, // 末尾パケットのバッファー int footer_size, // 末尾パケットのバッファー・サイズ HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル unsigned char *p_buf, // 計算済みのパリティ・ブロック unsigned char *g_buf, // GPU用 (GPUを使わない場合は NULLにすること) unsigned int unit_size) { unsigned char *packet_header, hash[HASH_SIZE]; wchar_t file_ext[EXT_LEN], *tmp_p; int rv, j, num; int exp_num, block_start, block_count, block_start_max, block_count_max; int repeat_max, packet_to, packet_from, packet_size, common_off; unsigned int time_last, prog_write; __int64 prog_num, prog_base; HANDLE hFile; // パリティ・ブロック計算に続いて経過表示する prog_write = source_num >> 4; // 計算で 94%、書き込みで 6% ぐらい if (prog_write == 0) prog_write = 1; prog_base = (__int64)(source_num + prog_write) * parity_num; // ブロックの合計掛け算個数 prog_num = source_num * parity_num; // パリティ・ブロック計算が終了した直後から始める time_last = GetTickCount(); if (recovery_path[MAX_LEN - 1] == 0){ // インデックス・ファイルを作るときだけ // パリティ・ブロックを含まないリカバリ・ファイルを書き込む //move_away_file(recovery_file); // 既存のファイルをどかす hFile = CreateFile(recovery_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE){ if (GetLastError() == ERROR_PATH_NOT_FOUND){ // Path not found (3) make_dir(recovery_file); // 途中のフォルダが存在しないのなら作成する hFile = CreateFile(recovery_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot create file, %s\n", recovery_file); return 1; } } if (!WriteFile(hFile, common_buf, common_size, &rv, NULL)){ print_win32_err(); CloseHandle(hFile); return 1; } if (!WriteFile(hFile, footer_buf, footer_size, &rv, NULL)){ print_win32_err(); CloseHandle(hFile); return 1; } CloseHandle(hFile); } // Recovery Slice packet 用のパケット・ヘッダーを作成しておく packet_header = p_buf - 160; // ソース・ブロック領域を利用する set_packet_header(packet_header, common_buf + 32, 4, block_size + 4); // リカバリ・ファイルの拡張子には指定されたものを使う file_ext[0] = 0; wcscpy(recovery_base, recovery_file); tmp_p = offset_file_name(recovery_base); tmp_p = wcsrchr(tmp_p, '.'); if (tmp_p != NULL){ if (wcslen(tmp_p) < EXT_LEN){ wcscpy(file_ext, tmp_p); // 拡張子を記録しておく *tmp_p = 0; // 拡張子を取り除く } } // ボリューム番号の桁数を求める block_start_max = calc_max_num(block_distri, &block_count_max); // リカバリ・ファイルを作成して共通パケットを書き込む block_start = 0; block_count = 0; exp_num = 1; for (num = 0; num < recovery_num; num++){ // リカバリ・ファイルのファイル名 switch (block_distri & 3){ case 0: // 同じ数なら block_count = parity_num / recovery_num; j = parity_num % recovery_num; // 割り切れない場合は if ((j > 0) && (num < j)) block_count++; break; case 1: // 倍々で異なる数にする if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる block_count = parity_num - block_start; } else { exp_num = recovery_limit & 0xFFFF; if (num >= 16){ block_count = exp_num; } else { block_count = ((unsigned int)recovery_limit >> 16) << num; if (block_count > exp_num) block_count = exp_num; } } break; case 2: // 1,2,4,8,16 と2の乗数なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { exp_num *= 2; } break; case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { switch (num % 4){ case 1: case 3: exp_num = exp_num * 2; break; case 2: exp_num = (exp_num / 2) * 5; break; } } break; } if (block_start + block_count > parity_num) block_count = parity_num - block_start; if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); } else { // QuickPar方式、volXX+YY swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, first_num + block_start, block_count_max, block_count, file_ext); } // リカバリ・ファイルを開く //move_away_file(recovery_path); // 既存のファイルをどかす if (rcv_hFile != NULL){ // 書庫ファイルに連結する場合は、後で読めるようにする hFile = CreateFile(recovery_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } else { // 書庫ファイルに連結しないので、読み込む必要が無い hFile = CreateFile(recovery_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot create file, %s\n", recovery_path); return 1; } if (rcv_hFile != NULL) rcv_hFile[num] = hFile; // ファイル・ハンドルを記録する // パケットの繰り返し回数を計算する repeat_max = 1; for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) repeat_max++; if ((packet_limit > 0) && (repeat_max > packet_limit)) repeat_max = packet_limit; // 繰り返し回数を制限する repeat_max *= packet_num; // リカバリ・ファイルの共通パケットの数 packet_from = 0; common_off = 0; // Recovery Slice packet は後から書き込む for (j = block_start; j < block_start + block_count; j++){ if (g_buf != NULL){ // GPUを使った場合 // CPUスレッドと GPUスレッドの計算結果を合わせる galois_align_xor(g_buf + (size_t)unit_size * j, p_buf, unit_size); } // パリティ・ブロックのチェックサムを検証する checksum16_return(p_buf, hash, unit_size - HASH_SIZE); if (memcmp(p_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){ printf("checksum mismatch, recovery slice %d\n", first_num + j); if (rcv_hFile == NULL) CloseHandle(hFile); return 1; } // Recovery Slice packet memcpy(p_buf - 68, packet_header, 64); // パケット・ヘッダーをコピーする rv = first_num + j; // 最初のパリティ・ブロック番号の分だけ足す memcpy(p_buf - 4, &rv, 4); // Recovery Slice の番号を書き込む data_md5(p_buf - 36, 36 + block_size, p_buf - 52); // パケットの MD5 を計算する if (!WriteFile(hFile, p_buf - 68, 68 + block_size, &rv, NULL)){ print_win32_err(); printf("file_write_data, recovery slice %d\n", first_num + j); if (rcv_hFile == NULL) CloseHandle(hFile); return 1; } p_buf += unit_size; // 経過表示 prog_num += prog_write; if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((int)((prog_num * 1000) / prog_base))){ if (rcv_hFile == NULL) CloseHandle(hFile); return 2; } time_last = GetTickCount(); } // どれだけの共通パケットを書き込むか packet_size = 0; packet_to = (int)((__int64)repeat_max * (j - block_start + 1) / block_count); while (packet_to - packet_from > 0){ memcpy(&rv, common_buf + (common_off + (packet_size + 8)), 4); // そのパケットのデータ・サイズを調べる packet_size += rv; packet_from++; } if (packet_size > 0){ // 共通パケットを書き込む if (!WriteFile(hFile, common_buf + common_off, packet_size, &rv, NULL)){ print_win32_err(); if (rcv_hFile == NULL) CloseHandle(hFile); return 1; } // オフセットが半分を超えたら戻しておく common_off += packet_size; if (common_off >= common_size) common_off -= common_size; } } block_start += block_count; // 末尾パケットを書き込む if (!WriteFile(hFile, footer_buf, footer_size, &rv, NULL)){ print_win32_err(); if (rcv_hFile == NULL) CloseHandle(hFile); return 1; } if (rcv_hFile == NULL){ // 後でファイル・ハンドルが必要なければ //FlushFileBuffers(hFile); // 書き込みが完了するのを待つ? // 今まで問題視されなかった訳だし、特に気にしなくていいかも・・・ CloseHandle(hFile); // ファイルを閉じる } } print_progress_done(); // 改行して行の先頭に戻しておく //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); return 0; } // 作成中のリカバリ・ファイルを削除する void delete_recovery_file( wchar_t *recovery_path, // 作業用 int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) int switch_p, // インデックス・ファイルを作らない HANDLE *rcv_hFile) // 各リカバリ・ファイルのハンドル { wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; int j, num; int exp_num, block_start, block_count, block_start_max, block_count_max; if (switch_p == 0) DeleteFile(recovery_file); // インデックス・ファイルを削除する // リカバリ・ファイルの拡張子には指定されたものを使う file_ext[0] = 0; wcscpy(recovery_base, recovery_file); tmp_p = offset_file_name(recovery_base); tmp_p = wcsrchr(tmp_p, '.'); if (tmp_p != NULL){ if (wcslen(tmp_p) < EXT_LEN){ wcscpy(file_ext, tmp_p); // 拡張子を記録しておく *tmp_p = 0; // 拡張子を取り除く } } // ボリューム番号の桁数を求める block_start_max = calc_max_num(block_distri, &block_count_max); // リカバリ・ファイルを削除する block_start = 0; block_count = 0; exp_num = 1; for (num = 0; num < recovery_num; num++){ // リカバリ・ファイルのファイル名 switch (block_distri & 3){ case 0: // 同じ数なら block_count = parity_num / recovery_num; j = parity_num % recovery_num; // 割り切れない場合は if ((j > 0) && (num < j)) block_count++; break; case 1: // 倍々で異なる数にする if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる block_count = parity_num - block_start; } else { exp_num = recovery_limit & 0xFFFF; if (num >= 16){ block_count = exp_num; } else { block_count = ((unsigned int)recovery_limit >> 16) << num; if (block_count > exp_num) block_count = exp_num; } } break; case 2: // 1,2,4,8,16 と2の乗数なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { exp_num *= 2; } break; case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { switch (num % 4){ case 1: case 3: exp_num = exp_num * 2; break; case 2: exp_num = (exp_num / 2) * 5; break; } } break; } if (block_start + block_count > parity_num) block_count = parity_num - block_start; if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); } else { // QuickPar方式、volXX+YY swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, first_num + block_start, block_count_max, block_count, file_ext); } block_start += block_count; if (rcv_hFile != NULL){ if (rcv_hFile[num] != NULL){ // ファイルが作成済みなら (開いていれば) CloseHandle(rcv_hFile[num]); rcv_hFile[num] = NULL; DeleteFile(recovery_path); // 途中までのリカバリ・ファイルを削除する } } else { DeleteFile(recovery_path); // 作成したリカバリ・ファイルを削除する } } } // リカバリ・ファイルのサイズを計算する void measure_recovery_file( wchar_t *recovery_path, // 作業用 (最初はコメントが入ってる) int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 int block_distri, // パリティ・ブロックの分配方法 int packet_num, // 共通パケットの数 int common_size, // 共通パケットのバッファー・サイズ int footer_size, // 末尾パケットのバッファー・サイズ int switch_p) // インデックス・ファイルを作らない { char ascii_buf[MAX_LEN * 3]; wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; int j, num; int exp_num, block_start, block_count, block_start_max, block_count_max; int footer_num, packet_count, repeat_max; __int64 file_size; get_file_name(recovery_file, recovery_base); // ファイル名だけにする footer_num = 1; if (recovery_path[0] != 0) footer_num++; total_file_size = 0; // リカバリ・ファイルの合計サイズを計算する if (switch_p == 0){ // Index File を先に表示する file_size = common_size + footer_size; total_file_size += file_size; packet_count = packet_num + footer_num; utf16_to_cp(recovery_base, ascii_buf, cp_output); printf("%13I64d %7d 0 : \"%s\"\n", file_size, packet_count, ascii_buf); } if (parity_num == 0) // パリティ・ブロックを作らない場合はここで終わる return; // リカバリ・ファイルの拡張子には指定されたものを使う file_ext[0] = 0; tmp_p = offset_file_name(recovery_base); tmp_p = wcsrchr(tmp_p, '.'); if (tmp_p != NULL){ if (wcslen(tmp_p) < EXT_LEN){ wcscpy(file_ext, tmp_p); // 拡張子を記録しておく *tmp_p = 0; // 拡張子を取り除く } } // ボリューム番号の桁数を求める block_start_max = calc_max_num(block_distri, &block_count_max); // リカバリ・ファイルを作成して共通パケットを書き込む block_start = 0; block_count = 0; exp_num = 1; for (num = 0; num < recovery_num; num++){ // リカバリ・ファイルのファイル名 switch (block_distri & 3){ case 0: // 同じ数なら block_count = parity_num / recovery_num; j = parity_num % recovery_num; // 割り切れない場合は if ((j > 0) && (num < j)) block_count++; break; case 1: // 倍々で異なる数にする if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる block_count = parity_num - block_start; } else { exp_num = recovery_limit & 0xFFFF; if (num >= 16){ block_count = exp_num; } else { block_count = ((unsigned int)recovery_limit >> 16) << num; if (block_count > exp_num) block_count = exp_num; } } break; case 2: // 1,2,4,8,16 と2の乗数なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { exp_num *= 2; } break; case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら block_count = exp_num; if (block_count >= recovery_limit){ block_count = recovery_limit; } else { switch (num % 4){ case 1: case 3: exp_num = exp_num * 2; break; case 2: exp_num = (exp_num / 2) * 5; break; } } break; } if (block_start + block_count > parity_num) block_count = parity_num - block_start; if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); } else { // QuickPar方式、volXX+YY swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, first_num + block_start, block_count_max, block_count, file_ext); } utf16_to_cp(recovery_path, ascii_buf, cp_output); // パケットの繰り返し回数を計算する repeat_max = 1; for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) repeat_max++; if ((packet_limit > 0) && (repeat_max > packet_limit)) repeat_max = packet_limit; // 繰り返し回数を制限する // リカバリ・ファイルの大きさを計算する file_size = (__int64)(68 + block_size) * block_count; file_size += (common_size * repeat_max) + footer_size; total_file_size += file_size; packet_count = repeat_max * packet_num; // リカバリ・ファイルの共通パケットの数 packet_count += footer_num + block_count; printf("%13I64d %7d %6d : \"%s\"\n", file_size, packet_count, block_count, ascii_buf); block_start += block_count; } } // ソース・ファイルを分割する (分割サイズは split_size で指定する) int split_files( file_ctx_c *files, int *cur_num, int *cur_id) // エラー発生時は、その時点でのファイル番号と分割番号が戻る { unsigned char buf[IO_SIZE]; wchar_t file_path[MAX_LEN], recovery_dir[MAX_LEN]; wchar_t *file_name, *file_ext; int num, id, split_num, split_max, total_num, dir_len; int name_len, ext_len, num2; unsigned int rv, len, split_left; unsigned int time_last, prog_num = 0; __int64 num8, file_left; HANDLE hFile, hFile_src; // 分割したファイルをどこに保存するか get_base_dir(recovery_file, recovery_dir); if (compare_directory(base_dir, recovery_dir) == 0){ // 分割先が同じ場所ならコピーしない recovery_dir[0] = 0; dir_len = base_len; } else { dir_len = (int)wcslen(recovery_dir); //printf_cp("\n save_path = %s \n", recovery_dir); } // うまく分割できるか確かめる total_num = 0; for (num = 0; num < file_num; num++){ if (files[num].size > (__int64)split_size){ num8 = (files[num].size + (__int64)split_size - 1) / split_size; if (num8 > 99999){ // 分割個数が 99999を超えたらエラー printf("too many split file, %d\n", num); *cur_num = -1; *cur_id = 0; return 1; } total_num += (int)num8; } else if (recovery_dir[0] != 0){ // 分割先が異なる場合だけコピーする total_num++; } } //printf("split total num = %d\n", total_num); if (total_num == 0) return 0; // 分割する必要なし // 分割したファイルが、ソース・ファイルを上書きしないかチェックする for (num = 0; num < file_num; num++){ // 拡張子を捜す ext_len = 0; file_name = list_buf + files[num].name; file_ext = wcsrchr(file_name, '.'); if (file_ext != NULL){ name_len = (int)(file_ext - file_name); ext_len = (int)wcslen(file_ext); if ((ext_len >= 4) && (ext_len <= 6)){ // 最長で ".99999" になる split_num = 0; for (id = 1; id < ext_len; id++){ if ((file_ext[id] < '0') || (file_ext[id] > '9')){ ext_len = 0; break; } split_num = split_num * 10 + (file_ext[id] - '0'); } // 分割先が異なる場合は、分割されるソース・ファイルを上書きしない if ((ext_len > 0) && (recovery_dir[0] != 0) && (files[num].size > split_size)) ext_len = 0; } else { ext_len = 0; } } if (ext_len > 0){ // 全て数字の拡張子を持つソース・ファイルがあるなら //printf_cp("\n risky name = %s \n", file_name); for (num2 = 0; num2 < file_num; num2++){ if (num2 == num) continue; if (_wcsnicmp(list_buf + files[num2].name, file_name, name_len) == 0){ //printf_cp(" match name = %s \n", list_buf + files[num2].name); num8 = (files[num2].size + (__int64)split_size - 1) / split_size; split_max = (int)num8; //printf("split_num = %d, split max = %d, ext_len = %d \n", split_num, split_max, ext_len); // 分割数的に問題なければ除外する if ((split_max == 1) || (split_num > split_max)) continue; // 分割サイズよりも小さなファイルは、数字の拡張子が付かない if (((split_max < 1000) && (ext_len >= 5)) || ((split_max < 10000) && (ext_len >= 6))) continue; // 拡張子の桁数が異なる // 上書きする危険性があるのでエラーにする printf_cp("split bad file, %s\n", file_name); *cur_num = -1; *cur_id = 0; return 1; } } } } // 分割サイズよりも大きなファイルだけ分割する printf("\n"); print_progress_text(0, "Spliting file"); time_last = GetTickCount(); for (num = 0; num < file_num; num++){ if (files[num].size > (__int64)split_size){ // 分割する file_left = files[num].size; num8 = (file_left + (__int64)split_size - 1) / split_size; split_num = (int)num8; if (split_num <= 999){ split_max = 3; // 標準では 001~999 の 999分割までだから 3桁 } else if (split_num <= 9999){ split_max = 4; } else { // ソース・ブロックは 32768個以下だから分割数もそれ以下のはず split_max = 5; } // ソース・ファイルを開く wcscpy(file_path, base_dir); wcscpy(file_path + base_len, list_buf + files[num].name); hFile_src = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile_src == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot open file, %s\n", list_buf + files[num].name); *cur_num = num; *cur_id = 0; return 1; } if (recovery_dir[0] != 0) wcscpy(file_path, recovery_dir); // 異なる場所に保存する for (id = 1; id <= split_num; id++){ swprintf(file_path + dir_len, _countof(file_path) - dir_len, L"%s.%0*d", list_buf + files[num].name, split_max, id); hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); CloseHandle(hFile_src); printf_cp("cannot create file, %s\n", file_path); *cur_num = num; *cur_id = id - 1; return 1; } // ファイルを分割サイズごとに書き込んでいく split_left = split_size; if ((__int64)split_left > file_left) split_left = (unsigned int)file_left; file_left -= split_left; while (split_left){ len = IO_SIZE; if (split_left < IO_SIZE) len = split_left; split_left -= len; if (!ReadFile(hFile_src, buf, len, &rv, NULL) || (len != rv)){ print_win32_err(); CloseHandle(hFile_src); CloseHandle(hFile); printf("ReadFile, input file %d\n", num); *cur_num = num; *cur_id = id; return 1; } if (!WriteFile(hFile, buf, len, &rv, NULL)){ print_win32_err(); CloseHandle(hFile_src); CloseHandle(hFile); printf("WriteFile, split file %d.%03d", num, id); *cur_num = num; *cur_id = id; return 1; } } CloseHandle(hFile); // 経過表示 prog_num++; if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((prog_num * 1000) / total_num)){ CloseHandle(hFile_src); *cur_num = num; *cur_id = id; return 2; } time_last = GetTickCount(); } } CloseHandle(hFile_src); } else if (recovery_dir[0] != 0){ // 小さなファイルはそのままコピーする // ソース・ファイルのパス wcscpy(file_path, base_dir); wcscpy(file_path + base_len, list_buf + files[num].name); // コピー先のパス wcscpy(recovery_dir + dir_len, list_buf + files[num].name); rv = CopyFile(file_path, recovery_dir, FALSE); if (rv != 0){ // 読み取り専用属性を解除しておく len = GetFileAttributes(recovery_dir); if ((len & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY)) != 0){ len &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); rv = SetFileAttributes(recovery_dir, len); } } if (rv == 0){ print_win32_err(); printf_cp("cannot copy file, %s\n", recovery_dir); *cur_num = num; *cur_id = 0; return 1; } recovery_dir[dir_len] = 0; // ディレクトリに戻しておく // 経過表示 prog_num++; if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress((prog_num * 1000) / total_num)){ *cur_num = num; *cur_id = 0; return 2; } time_last = GetTickCount(); } } } print_progress_done(); // 改行して行の先頭に戻しておく return 0; } // 分割されたソース・ファイルを削除する void delete_split_files( file_ctx_c *files, int max_num, int max_id) // エラー発生時のファイル番号と分割番号 { wchar_t file_path[MAX_LEN], recovery_dir[MAX_LEN]; int num, id, split_num, split_max, dir_len; __int64 num8, file_left; // 分割したファイルをどこに保存するか get_base_dir(recovery_file, recovery_dir); if (compare_directory(base_dir, recovery_dir) == 0){ // 分割先が同じ場所ならコピーしない recovery_dir[0] = 0; dir_len = base_len; } else { dir_len = (int)wcslen(recovery_dir); } // 分割サイズよりも大きなファイルだけ分割する for (num = 0; num <= max_num; num++){ if (files[num].size > (__int64)split_size){ // 分割されたファイルを削除する file_left = files[num].size; num8 = (file_left + (__int64)split_size - 1) / split_size; split_num = (int)num8; // 分割個数は 99999 以下のはず if (split_num <= 999){ split_max = 3; // 標準では 001~999 の 999分割までだから 3桁 } else if (split_num <= 9999){ split_max = 4; } else { // if (split_num <= 99999){ split_max = 5; } if (num == max_num) split_num = max_id; // 中断した所まで削除する for (id = 1; id <= split_num; id++){ if (recovery_dir[0] != 0){ wcscpy(file_path, recovery_dir); // 異なる場所に保存した } else { wcscpy(file_path, base_dir); } swprintf(file_path + dir_len, _countof(file_path) - dir_len, L"%s.%0*d", list_buf + files[num].name, split_max, id); if (!DeleteFile(file_path)) // 既に分割してるソース・ファイルを削除する return; // 削除に失敗したら出る } } else if (recovery_dir[0] != 0){ // 別の場所にコピーした場合だけ削除する wcscpy(recovery_dir + dir_len, list_buf + files[num].name); // コピー先のパス if (!DeleteFile(recovery_dir)) // コピーしたソース・ファイルを削除する return; // 削除に失敗したら出る recovery_dir[dir_len] = 0; // ディレクトリに戻しておく } } } #define ZIP_SEARCH_SIZE 1022 // リカバリ・ファイルをソース・ファイルの末尾にくっつける // フォーマットによってはソース・ファイルが使えなくなる恐れがあるので、制限する? // 前方から読み込んでいくストリーム形式だと問題ないが、フッターを先に読む形式だと無理 // 7z 書庫は問題ないが、ZIP 書庫は末尾を工夫しないといけない。 // bz2 と gz は 7-Zip で展開時にエラーが表示される・・・ // EXE や DLL ファイルはチェックサムが変わるので、読み込みエラーが発生するかも。 // JPEG, GIF, PNG, MP3 などは問題ないっぽいけど、リカバリ・レコードを埋め込む必要が無い。 int append_recovery_file( wchar_t *file_name, // ソース・ファイルの名前 HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル int no_index) // インデックス・ファイルが存在しない { unsigned char buf[IO_SIZE], header[ZIP_SEARCH_SIZE]; wchar_t file_path[MAX_LEN], save_path[MAX_LEN]; int num, header_size, header_off; unsigned int rv, len, attach_flag; unsigned int time_last; HANDLE hFileWrite = NULL, hFileRead = NULL; LARGE_INTEGER qwi, orig_size; // Quad Word Integer // 結合したファイルをどこに保存するか attach_flag = 0; printf("\n"); print_progress_text(0, "Appending recovery record"); time_last = GetTickCount(); get_base_dir(recovery_file, save_path); if (compare_directory(base_dir, save_path) != 0){ // 結合先が異なる場所ならコピーする wcscpy(file_path, base_dir); wcscat(file_path, file_name); wcscat(save_path, file_name); if (CopyFile(file_path, save_path, FALSE) == 0){ print_win32_err(); printf_cp("cannot copy file, %s\n", save_path); return 1; } attach_flag = 0x02000000; // コピーした印 } else { wcscpy(save_path, base_dir); wcscat(save_path, file_name); attach_flag = 0x01000000; // ソース・ファイルが同じ場所の印 } // 読み取り専用属性を解除しておく len = GetFileAttributes(save_path); if ((len & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY)) != 0){ len &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); SetFileAttributes(save_path, len); } // ソース・ファイルを開く hFileWrite = CreateFile(save_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFileWrite == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot open file, %s\n", save_path); return 1; } // 対応するフォーマットであることを確認する // 7z = 先頭 6-byte の Signature で判定する // zip = 先頭 4-byte の local file header signature と // 末尾 22-byte の end of central directory record で判定する if (!ReadFile(hFileWrite, buf, 28, &len, NULL) || (len != 28)){ print_win32_err(); goto error_end; } if (((unsigned int *)buf)[0] == 0x04034b50){ // 末尾を何バイト読み込むか(ZIPのコメントに対応する) if (total_file_size < ZIP_SEARCH_SIZE){ header_size = (int)total_file_size - 32; // 最低でも 32 + 22 = 54 バイトはあるはず } else { header_size = ZIP_SEARCH_SIZE; } // ファイルの末尾 header_size バイトを読み込む qwi.QuadPart = - header_size; if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_END)){ print_win32_err(); goto error_end; } if (!ReadFile(hFileWrite, header, header_size, &len, NULL) || (len != header_size)){ print_win32_err(); goto error_end; } // 後ろから Signature を探す num = header_size - 22; while (num >= 0){ if (((unsigned int *)(header + num))[0] == 0x06054b50){ memcpy(&len, header + num + 12, 4); // ヘッダーに記録されてるサイズを確認する memcpy(&rv, header + num + 16, 4); if (rv + len + header_size - num == total_file_size){ attach_flag |= 0x10000000; header_off = num; header_size -= num; break; } } else if (((unsigned int *)(header + num))[0] == 0x06064b50){ memcpy(&len, header + num + 40, 4); // ZIP64ヘッダーに記録されてるサイズを確認する memcpy(&orig_size.QuadPart, header + num + 48, 8); if (len + orig_size.QuadPart + header_size - num == total_file_size){ attach_flag |= 0x10000000; header_off = num; header_size -= num; break; } } num--; } } else if ((((unsigned short *)buf)[0] == 0x7A37) && (((unsigned int *)(buf + 2))[0] == 0x1C27AFBC)){ __int64 size1, size2; memcpy(&size1, buf + 12, 8); // ヘッダーに記録されてるサイズを確認する memcpy(&size2, buf + 20, 8); if (32 + size1 + size2 == total_file_size) attach_flag |= 0x20000000; } if ((attach_flag & 0x30000000) == 0){ printf("\ninvalid archive format\n"); goto error_end; } // ファイルの位置を末尾にする qwi.QuadPart = 0; if (!SetFilePointerEx(hFileWrite, qwi, &orig_size, FILE_END)){ print_win32_err(); goto error_end; } //printf("\n qwi.QuadPart = %I64d\n", orig_size.QuadPart); if (no_index == 0){ // インデックス・ファイルを開く hFileRead = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFileRead == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot open file, %s\n", recovery_file); goto error_end; } // 末尾に追加していく do { if (!ReadFile(hFileRead, buf, IO_SIZE, &len, NULL)){ print_win32_err(); goto error_end; } if (len == 0) break; attach_flag |= len; if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ print_win32_err(); goto error_end; } } while (len > 0); CloseHandle(hFileRead); hFileRead = NULL; } // リカバリ・ファイルを開く for (num = 0; num < recovery_num; num++){ // 経過表示 if (GetTickCount() - time_last >= UPDATE_TIME){ if (print_progress(((num + 1) * 1000) / recovery_num)){ goto error_end; } time_last = GetTickCount(); } // ファイルの位置を先頭にする qwi.QuadPart = 0; if (!SetFilePointerEx(rcv_hFile[num], qwi, NULL, FILE_BEGIN)){ print_win32_err(); goto error_end; } // 末尾に追加していく do { if (!ReadFile(rcv_hFile[num], buf, IO_SIZE, &len, NULL)){ print_win32_err(); goto error_end; } if (len == 0) break; attach_flag |= len; if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ print_win32_err(); goto error_end; } } while (len > 0); } // ZIP 書庫なら末尾にヘッダーを追加する if (attach_flag & 0x10000000){ if (!WriteFile(hFileWrite, header + header_off, header_size, &rv, NULL) || (rv != header_size)){ print_win32_err(); goto error_end; } } print_progress_done(); // 改行して行の先頭に戻しておく CloseHandle(hFileWrite); return 0; error_end: // エラー発生時 if (attach_flag & 0x02000000){ // コピーしたファイルを削除する if (hFileWrite != NULL){ CloseHandle(hFileWrite); hFileWrite = NULL; } DeleteFile(save_path); } else if ((attach_flag & 0x01FFFFFF) > 0x01000000){ // 追加したバイトを取り除く if (SetFilePointerEx(hFileWrite, orig_size, NULL, FILE_BEGIN) != 0) SetEndOfFile(hFileWrite); } if (hFileWrite != NULL) CloseHandle(hFileWrite); if (hFileRead != NULL) CloseHandle(hFileRead); return 1; }