00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include
00020 #include
00021 #include
00022 #include
00023 #include
00024 #include
00025 #include
00026 #include
00027 #include
00028 #ifdef _MSC_VER
00029 #include
00030 #else
00031 #include
00032 #endif
00033
00034 #include "par2check.h"
00035
00036 typedef struct _par2_packet_hdr {
00037 unsigned char magic[8];
00038 guint64 length;
00039 unsigned char md5_hash[16];
00040 unsigned char recovery_id[16];
00041 unsigned char type[16];
00042 } PAR2PacketHDR;
00043
00044 typedef struct _par2_file_descriptor {
00045 unsigned char file_id[16];
00046 unsigned char md5_hash[16];
00047 unsigned char md5_16k_hash[16];
00048 guint64 length;
00049 } PAR2FileDescriptor;
00050
00051 typedef struct _par2_file_slice_checksum {
00052 unsigned char file_id[16];
00053 } PAR2FileSliceChecksum;
00054
00055 typedef struct _par2_main {
00056 guint64 blocksize;
00057 guint32 num_files;
00058 } PAR2Main;
00059
00060 #ifdef WIN32
00061 #pragma pack(push, 1)
00062 #define PACKED
00063 #else
00064 #define PACKED __attribute__ ((packed))
00065 #endif
00066
00067 typedef struct MAGIC MAGIC;
00068 typedef struct PACKETTYPE PACKETTYPE;
00069
00070 struct MAGIC {guint8 magic[8];} PACKED;
00071 struct PACKETTYPE {guint8 type[16];} PACKED;
00072
00073 MAGIC magic_hdr = {{'P', 'A', 'R', '2', '\0','P', 'K', 'T'}};
00074 PACKETTYPE fileverificationpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'I', 'F', 'S', 'C', '\0','\0','\0','\0'}};
00075 PACKETTYPE filedescriptionpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c' }};
00076 PACKETTYPE mainpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'M', 'a', 'i', 'n', '\0','\0','\0','\0'}};
00077 PACKETTYPE recoveryblockpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'R', 'e', 'c', 'v', 'S', 'l', 'i', 'c' }};
00078 PACKETTYPE creatorpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'C', 'r', 'e', 'a', 't', 'o', 'r', '\0'}};
00079
00080 #if 0
00081 #define PRINT(...) g_print(__VA_ARGS__)
00082 #else
00083 #define PRINT(x, ...)
00084 #endif
00085
00086 gboolean
00087 test_par2_packet_hdr(const unsigned char *data)
00088 {
00089 if (!memcmp(data, &magic_hdr, sizeof(magic_hdr))) {
00090 return TRUE;
00091 } else {
00092 return FALSE;
00093 }
00094 }
00095
00096 gboolean
00097 test_par2_is_file_description_type(const unsigned char *data)
00098 {
00099 if (!memcmp(data, &filedescriptionpacket_type, sizeof(filedescriptionpacket_type))) {
00100 return TRUE;
00101 } else {
00102 return FALSE;
00103 }
00104 }
00105
00106 gboolean
00107 test_par2_is_file_verification_type(const unsigned char *data)
00108 {
00109 if (!memcmp(data, &fileverificationpacket_type, sizeof(fileverificationpacket_type))) {
00110 return TRUE;
00111 } else {
00112 return FALSE;
00113 }
00114 }
00115
00116 gboolean
00117 test_par2_is_main_packet_type(const unsigned char *data)
00118 {
00119 if (!memcmp(data, &mainpacket_type, sizeof(mainpacket_type))) {
00120 return TRUE;
00121 } else {
00122 return FALSE;
00123 }
00124 }
00125
00126 gboolean
00127 test_par2_is_recovery_block_packet_type(const unsigned char *data)
00128 {
00129 if (!memcmp(data, &recoveryblockpacket_type, sizeof(recoveryblockpacket_type))) {
00130 return TRUE;
00131 } else {
00132 return FALSE;
00133 }
00134 }
00135
00136 gboolean
00137 test_par2_is_creator_packet_type(const unsigned char *data)
00138 {
00139 if (!memcmp(data, &creatorpacket_type, sizeof(creatorpacket_type))) {
00140 return TRUE;
00141 } else {
00142 return FALSE;
00143 }
00144 }
00145
00155 gboolean
00156 par2_get_md5_chunks(const char *par2file, guint64 *block_size, GList **files)
00157 {
00158 int fp;
00159 PAR2File *file = NULL;
00160
00161 g_assert(block_size);
00162 g_assert(par2file);
00163 g_assert(files);
00164
00165 *files = NULL;
00166
00167 #ifdef WIN32
00168 fp = open(par2file, O_RDONLY | O_BINARY);
00169 #else
00170 fp = open(par2file, O_RDONLY);
00171 #endif
00172 if (!fp) {
00173 return FALSE;
00174 }
00175
00176 do {
00177 PAR2PacketHDR hdr;
00178 int len_remaining_data;
00179 int len;
00180 void *data;
00181
00182 len = read(fp, &hdr, sizeof(hdr));
00183 if (len == 0) {
00184
00185 close(fp);
00186 return TRUE;
00187 }
00188
00189 if (len != sizeof(hdr)) {
00190 close(fp);
00191 return FALSE;
00192 }
00193
00194 if (!test_par2_packet_hdr(hdr.magic)) {
00195 PRINT("File is not a PAR2 file\n");
00196 close(fp);
00197 return FALSE;
00198 }
00199
00200 if (test_par2_is_file_description_type(hdr.type)) {
00201 PAR2FileDescriptor desc;
00202 guint64 len_filename;
00203 char *filename;
00204
00205 PRINT("File description found\n");
00206
00207 if (read(fp, &desc, sizeof(desc)) != sizeof(desc)) {
00208 close(fp);
00209 return FALSE;
00210 }
00211
00212 PRINT("file size = %lli\n", desc.length);
00213
00214 len_filename = hdr.length - sizeof(hdr) - sizeof(desc);
00215 filename = g_slice_alloc0((int) len_filename + 1);
00216 if (read(fp, filename, (unsigned int) len_filename) != (unsigned int) len_filename) {
00217 close(fp);
00218 g_slice_free1((int) len_filename + 1, filename);
00219 return FALSE;
00220 }
00221
00222 PRINT("filename = %s\n", filename);
00223
00224 g_assert(file == NULL);
00225 file = g_slice_new0(PAR2File);
00226
00227 strncpy((char*) file->filename, filename, sizeof(file->filename));
00228 memcpy(&file->file_id, desc.file_id, sizeof(desc.file_id));
00229 memcpy(&file->md5_hash_total, desc.md5_hash, sizeof(desc.md5_hash));
00230 memcpy(&file->md5_hash_16k, desc.md5_16k_hash, sizeof(desc.md5_hash));
00231 file->file_size = desc.length;
00232
00233 *files = g_list_append(*files, file);
00234
00235 g_slice_free1((int) len_filename + 1, filename);
00236
00237 continue;
00238 }
00239
00240 if (test_par2_is_file_verification_type(hdr.type)) {
00241 PAR2FileSliceChecksum checksum;
00242 int num_slices;
00243 int i;
00244
00245 PRINT("File verification found\n");
00246
00247 if (read(fp, &checksum, sizeof(checksum)) != sizeof(checksum)) {
00248 close(fp);
00249 return FALSE;
00250 }
00251
00252 num_slices = (int) hdr.length - sizeof(hdr) - sizeof(checksum);
00253
00254 g_assert(num_slices % 20 == 0);
00255
00256 num_slices /= 20;
00257
00258 for (i = 0; i 00259 PAR2FileSliceChecksumPart slice;
00260 PAR2FileSliceChecksumPart *slice_dup;
00261 int j;
00262
00263 if (read(fp, &slice, sizeof(slice)) != sizeof(slice)) {
00264 close(fp);
00265 return FALSE;
00266 }
00267
00268 g_assert(file != NULL);
00269
00270 slice_dup = g_slice_new0(PAR2FileSliceChecksumPart);
00271 memcpy(slice_dup, &slice, sizeof(PAR2FileSliceChecksumPart));
00272
00273 file->slices = g_list_append(file->slices, slice_dup);
00274
00275 PRINT("MD5 = ", slice.md5_hash);
00276 for (j = 0; j 00277 PRINT("%02x", slice.md5_hash[j]);
00278 }
00279 PRINT("\n");
00280 }
00281
00282 file = NULL;
00283
00284 continue;
00285 }
00286
00287 if (test_par2_is_main_packet_type(hdr.type)) {
00288 PAR2Main main_data;
00289 guint64 len;
00290 char *data;
00291
00292 if (read(fp, &main_data, sizeof(main_data)) != sizeof(main_data)) {
00293 close(fp);
00294 return FALSE;
00295 }
00296
00297 PRINT("num files = %i\n", main_data.num_files);
00298 PRINT("blocksize = %lli\n", main_data.blocksize);
00299
00300 *block_size = main_data.blocksize;
00301
00302
00303 len = hdr.length - sizeof(hdr) - sizeof(main_data);
00304 data = g_slice_alloc0((int) len);
00305 if (read(fp, data, (int) len) != (int) len) {
00306 close(fp);
00307 g_slice_free1((int) len, data);
00308 return FALSE;
00309 }
00310
00311 g_slice_free1((int) len, data);
00312
00313 continue;
00314 }
00315
00316 if (test_par2_is_recovery_block_packet_type(hdr.type)) {
00317
00318 int len_remaining_data = (int) hdr.length - sizeof(hdr);
00319 void *data = g_slice_alloc0(len_remaining_data);
00320 if (read(fp, data, len_remaining_data) != len_remaining_data) {
00321 close(fp);
00322 g_slice_free1(len_remaining_data, data);
00323 return FALSE;
00324 }
00325
00326 g_slice_free1(len_remaining_data, data);
00327
00328 continue;
00329 }
00330
00331 if (test_par2_is_creator_packet_type(hdr.type)) {
00332
00333 int len_remaining_data = (int) hdr.length - sizeof(hdr);
00334 void *data = g_slice_alloc0(len_remaining_data);
00335 if (read(fp, data, len_remaining_data) != len_remaining_data) {
00336 close(fp);
00337 g_slice_free1(len_remaining_data, data);
00338 return FALSE;
00339 }
00340
00341 g_slice_free1(len_remaining_data, data);
00342
00343 continue;
00344 }
00345
00346 PRINT("Unknown PAR2 type: %s\n", hdr.type + 8);
00347
00348
00349 len_remaining_data = (int) hdr.length - sizeof(hdr);
00350 data = g_slice_alloc0(len_remaining_data);
00351 if (read(fp, data, len_remaining_data) != len_remaining_data) {
00352 close(fp);
00353 g_slice_free1(len_remaining_data, data);
00354 return FALSE;
00355 }
00356 g_slice_free1(len_remaining_data, data);
00357 } while (TRUE);
00358
00359 PRINT("\n\n");
00360
00361 close(fp);
00362 return TRUE;
00363 }
00364
00365 gboolean
00366 par2_test_file(const char *directory, PAR2File *par2file, guint64 blocksize, int *num_blocks_found, int *num_blocks_missing)
00367 {
00368 int fp;
00369 char *filename;
00370 char data[4];
00371 int count = 0;
00372 MD5_CTX ctx;
00373 int pos = 0;
00374
00375 g_assert(num_blocks_found);
00376 g_assert(num_blocks_missing);
00377
00378 *num_blocks_found = 0;
00379 *num_blocks_missing = 0;
00380
00381 filename = g_build_filename(directory, G_DIR_SEPARATOR_S, par2file->filename, NULL);
00382 #ifdef WIN32
00383 fp = open(filename, O_RDONLY | O_BINARY);
00384 #else
00385 fp = open(filename, O_RDONLY);
00386 #endif
00387 if (!fp) {
00388 return FALSE;
00389 }
00390
00391 if (!MD5_Init(&ctx)) {
00392 g_print("MD5_Init FAILED\n");
00393 return FALSE;
00394 }
00395
00396
00397 do {
00398 int len;
00399
00400 memset(&data, 0, sizeof(data));
00401 len = read(fp, &data, sizeof(data));
00402 if (len == -1) {
00403 close(fp);
00404 return FALSE;
00405 }
00406
00407 if (len sizeof(data)) {
00408 if (len == 0 && count == 0) {
00409
00410 break;
00411 }
00412
00413
00414 len = sizeof(data);
00415 }
00416
00417 if (!MD5_Update(&ctx, data, sizeof(data))) {
00418 g_print("MD5_Update FAILED\n");
00419 return FALSE;
00420 }
00421
00422 count += len;
00423 if (count == blocksize) {
00424
00425 unsigned char md5[16];
00426 PAR2FileSliceChecksumPart *slice;
00427 int j;
00428
00429 if (!MD5_Final((unsigned char*) &md5, &ctx)) {
00430 g_print("MD5_Final FAILED\n");
00431 return FALSE;
00432 }
00433
00434 slice = (PAR2FileSliceChecksumPart*) g_list_nth(par2file->slices, pos)->data;
00435
00436 if (!memcmp(slice->md5_hash, md5, sizeof(md5))) {
00437
00438 *num_blocks_found += 1;
00439 } else {
00440
00441 *num_blocks_missing += 1;
00442 }
00443
00444 PRINT("\tMD5 detected = ");
00445
00446 for (j = 0; j 00447 PRINT("%02x", md5[j]);
00448 }
00449 PRINT("\n");
00450
00451 pos++;
00452 count = 0;
00453 MD5_Init(&ctx);
00454 }
00455 } while (TRUE);
00456
00457 close(fp);
00458
00459 return TRUE;
00460 }