Files
MultiPar/source/par2j/create.c
2024-11-30 13:06:17 +09:00

2251 lines
80 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// create.c
// Copyright : 2024-11-30 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // Windows 7 or later
#endif
#include <process.h>
#include <stdio.h>
#include <windows.h>
#include "common2.h"
#include "phmd5.h"
#include "md5_crc.h"
#include "version.h"
#include "gf16.h"
#include "create.h"
//#define TIMER // 実験用
#ifdef TIMER
#include <time.h>
static double time_sec, time_speed;
#endif
// ソート時に項目を比較する
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
clock_t time_start = clock();
#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 = clock() - time_start;
time_sec = (double)time_start / CLOCKS_PER_SEC;
if (time_sec > 0){
time_speed = (double)total_file_size / (time_sec * 1048576);
} else {
time_speed = 0;
}
printf("hash %.3f sec, %.0f MB/s\n", time_sec, time_speed);
#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
clock_t time_start = clock();
#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 = clock() - time_start;
time_sec = (double)time_start / CLOCKS_PER_SEC;
if (time_sec > 0){
time_speed = (double)total_file_size / (time_sec * 1048576);
} else {
time_speed = 0;
}
printf("hash %.3f sec, %.0f MB/s\n", time_sec, time_speed);
#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
clock_t time_start = clock();
#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 = clock() - time_start;
printf("hash %.3f sec\n", (double)time_start / CLOCKS_PER_SEC);
#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
clock_t time_start = clock();
#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 = clock() - time_start;
printf("write %.3f sec\n", (double)time_start / CLOCKS_PER_SEC);
#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);
wcscpy(file_path, file_name); // 比較用に拡張子を取り除く
file_path[name_len] = 0;
for (num2 = 0; num2 < file_num; num2++){
if (num2 == num)
continue;
if (_wcsicmp(list_buf + files[num2].name, file_path) == 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", list_buf + files[num2].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; // 標準では 001999 の 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; // 標準では 001999 の 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;
}