par2check.c

Go to the documentation of this file.
00001 /*
00002  Copyright (C) 2005-2007 Erik van Pienbroek
00003 
00004  This program is free software; you can redistribute it and/or modify
00005  it under the terms of the GNU General Public License as published by
00006  the Free Software Foundation; either version 2 of the License, or
00007  (at your option) any later version.
00008 
00009  This program is distributed in the hope that it will be useful,
00010  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00012  GNU General Public License for more details.
00013 
00014  You should have received a copy of the GNU General Public License
00015  along with this program; if not, write to the Free Software
00016  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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];         // md5 sum of the whole PAR2 packet (from this point on)
00040     unsigned char recovery_id[16];      // This should be the same for all the PAR2 packets in a set
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];         // hash from the entire file
00047     unsigned char md5_16k_hash[16];     // hash from the first 16KB of the file
00048     guint64  length;                    // file size
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             // end of file
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             // Ignore the rest of this packet
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             // We aren't interested in this part of the PAR2 file
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             // We aren't interested in this part of the PAR2 file
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         // We aren't interested in the other parts of the PAR2 file
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     // Keep on reading blocks of 4 bytes until we've reached the blocksize
00397     do {
00398         int len;
00399 
00400         memset(&data, 0, sizeof(data));
00401         len = read(fp, &data, sizeof(data));
00402         if (len == -1) {    // Error occured
00403             close(fp);
00404             return FALSE;
00405         }
00406 
00407         if (len sizeof(data)) {
00408             if (len == 0 && count == 0) {
00409                 // No need to pad
00410                 break;
00411             }
00412 
00413             // Padding required
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             // Enough data read, verify the MD5
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                 // Good part found!
00438                 *num_blocks_found += 1;
00439             } else {
00440                 // Bad part found
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 }

Generated on Sun Oct 12 01:45:30 2008 for NNTPGrab by  1.5.4