~ubuntu-branches/debian/experimental/nzbget/experimental

« back to all changes in this revision

Viewing changes to lib/par2/par2creatorsourcefile.cpp

  • Committer: Package Import Robot
  • Author(s): Andreas Moog
  • Date: 2014-12-25 12:58:06 UTC
  • mfrom: (1.2.1) (3.1.4 sid)
  • Revision ID: package-import@ubuntu.com-20141225125806-vnzgajhm7mju9933
Tags: 14.1+dfsg-1
* New Upstream release (Closes: #768863)
* debian/patches:
  - Remove 0010_unnecessary_gcryptdep.patch, included upstream
  - Refresh remaining patches
* debian/control:
  - Remove no longer needed build-depends on libpar2-dev and libsigc++-dev
* debian/nzbget.conf
  - Update sample configuration file to include new options introduced by
    new upstream version.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//  This file is part of par2cmdline (a PAR 2.0 compatible file verification and
 
2
//  repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
 
3
//
 
4
//  Copyright (c) 2003 Peter Brian Clements
 
5
//
 
6
//  par2cmdline is free software; you can redistribute it and/or modify
 
7
//  it under the terms of the GNU General Public License as published by
 
8
//  the Free Software Foundation; either version 2 of the License, or
 
9
//  (at your option) any later version.
 
10
//
 
11
//  par2cmdline is distributed in the hope that it will be useful,
 
12
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
//  GNU General Public License for more details.
 
15
//
 
16
//  You should have received a copy of the GNU General Public License
 
17
//  along with this program; if not, write to the Free Software
 
18
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
19
 
 
20
#include "par2cmdline.h"
 
21
 
 
22
#ifdef _MSC_VER
 
23
#ifdef _DEBUG
 
24
#undef THIS_FILE
 
25
static char THIS_FILE[]=__FILE__;
 
26
#define new DEBUG_NEW
 
27
#endif
 
28
#endif
 
29
 
 
30
Par2CreatorSourceFile::Par2CreatorSourceFile(void)
 
31
{
 
32
  descriptionpacket = 0;
 
33
  verificationpacket = 0;
 
34
  diskfile = 0;
 
35
  blockcount = 0;
 
36
  //diskfilename;
 
37
  //parfilename;
 
38
  contextfull = 0;
 
39
}
 
40
 
 
41
Par2CreatorSourceFile::~Par2CreatorSourceFile(void)
 
42
{
 
43
  delete descriptionpacket;
 
44
  delete verificationpacket;
 
45
  delete diskfile;
 
46
  delete contextfull;
 
47
}
 
48
 
 
49
// Open the source file, compute the MD5 Hash of the whole file and the first
 
50
// 16k of the file, and then compute the FileId and store the results
 
51
// in a file description packet and a file verification packet.
 
52
 
 
53
bool Par2CreatorSourceFile::Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation)
 
54
{
 
55
  // Get the filename and filesize
 
56
  diskfilename = extrafile.FileName();
 
57
  filesize = extrafile.FileSize();
 
58
 
 
59
  // Work out how many blocks the file will be sliced into
 
60
  blockcount = (u32)((filesize + blocksize-1) / blocksize);
 
61
  
 
62
  // Determine what filename to record in the PAR2 files
 
63
  string::size_type where;
 
64
  if (string::npos != (where = diskfilename.find_last_of('\\')) ||
 
65
      string::npos != (where = diskfilename.find_last_of('/')))
 
66
  {
 
67
    parfilename = diskfilename.substr(where+1);
 
68
  }
 
69
  else
 
70
  {
 
71
    parfilename = diskfilename;
 
72
  }
 
73
 
 
74
  // Create the Description and Verification packets
 
75
  descriptionpacket = new DescriptionPacket;
 
76
  descriptionpacket->Create(parfilename, filesize);
 
77
 
 
78
  verificationpacket = new VerificationPacket;
 
79
  verificationpacket->Create(blockcount);
 
80
 
 
81
  // Create the diskfile object
 
82
  diskfile  = new DiskFile;
 
83
 
 
84
  // Open the source file
 
85
  if (!diskfile->Open(diskfilename, filesize))
 
86
    return false;
 
87
 
 
88
  // Do we want to defer the computation of the full file hash, and 
 
89
  // the block crc and hashes. This is only permitted if there
 
90
  // is sufficient memory available to create all recovery blocks
 
91
  // in one pass of the source files (i.e. chunksize == blocksize)
 
92
  if (deferhashcomputation)
 
93
  {
 
94
    // Initialise a buffer to read the first 16k of the source file
 
95
    size_t buffersize = 16 * 1024;
 
96
    if (buffersize > filesize)
 
97
      buffersize = (size_t)filesize;
 
98
    char *buffer = new char[buffersize];
 
99
 
 
100
    // Read the data from the file
 
101
    if (!diskfile->Read(0, buffer, buffersize))
 
102
    {
 
103
      diskfile->Close();
 
104
      delete [] buffer;
 
105
      return false;
 
106
    }
 
107
 
 
108
    // Compute the hash of the data read from the file
 
109
    MD5Context context;
 
110
    context.Update(buffer, buffersize);
 
111
    delete [] buffer;
 
112
    MD5Hash hash;
 
113
    context.Final(hash);
 
114
 
 
115
    // Store the hash in the descriptionpacket and compute the file id
 
116
    descriptionpacket->Hash16k(hash);
 
117
 
 
118
    // Compute the fileid and store it in the verification packet.
 
119
    descriptionpacket->ComputeFileId();
 
120
    verificationpacket->FileId(descriptionpacket->FileId());
 
121
 
 
122
    // Allocate an MD5 context for computing the file hash
 
123
    // during the recovery data generation phase
 
124
    contextfull = new MD5Context;
 
125
  }
 
126
  else
 
127
  {
 
128
    // Initialise a buffer to read the source file
 
129
    size_t buffersize = 1024*1024;
 
130
    if (buffersize > min(blocksize,filesize))
 
131
      buffersize = (size_t)min(blocksize,filesize);
 
132
    char *buffer = new char[buffersize];
 
133
 
 
134
    // Get ready to start reading source file to compute the hashes and crcs
 
135
    u64 offset = 0;
 
136
    u32 blocknumber = 0;
 
137
    u64 need = blocksize;
 
138
 
 
139
    MD5Context filecontext;
 
140
    MD5Context blockcontext;
 
141
    u32        blockcrc = 0;
 
142
 
 
143
    // Whilst we have not reached the end of the file
 
144
    while (offset < filesize)
 
145
    {
 
146
      // Work out how much we can read
 
147
      size_t want = (size_t)min(filesize-offset, (u64)buffersize);
 
148
 
 
149
      // Read some data from the file into the buffer
 
150
      if (!diskfile->Read(offset, buffer, want))
 
151
      {
 
152
        diskfile->Close();
 
153
        delete [] buffer;
 
154
        return false;
 
155
      }
 
156
 
 
157
      // If the new data passes the 16k boundary, compute the 16k hash for the file
 
158
      if (offset < 16384 && offset + want >= 16384)
 
159
      {
 
160
        filecontext.Update(buffer, (size_t)(16384-offset));
 
161
 
 
162
        MD5Context temp = filecontext;
 
163
        MD5Hash hash;
 
164
        temp.Final(hash);
 
165
 
 
166
        // Store the 16k hash in the file description packet
 
167
        descriptionpacket->Hash16k(hash);
 
168
 
 
169
        if (offset + want > 16384)
 
170
        {
 
171
          filecontext.Update(&buffer[16384-offset], (size_t)(offset+want)-16384);
 
172
        }
 
173
      }
 
174
      else
 
175
      {
 
176
        filecontext.Update(buffer, want);
 
177
      }
 
178
 
 
179
      // Get ready to update block hashes and crcs
 
180
      u32 used = 0;
 
181
 
 
182
      // Whilst we have not used all of the data we just read
 
183
      while (used < want)
 
184
      {
 
185
        // How much of it can we use for the current block
 
186
        u32 use = (u32)min(need, (u64)(want-used));
 
187
 
 
188
        blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, use, &buffer[used]);
 
189
        blockcontext.Update(&buffer[used], use);
 
190
 
 
191
        used += use;
 
192
        need -= use;
 
193
 
 
194
        // Have we finished the current block
 
195
        if (need == 0)
 
196
        {
 
197
          MD5Hash blockhash;
 
198
          blockcontext.Final(blockhash);
 
199
 
 
200
          // Store the block hash and block crc in the file verification packet.
 
201
          verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
 
202
 
 
203
          blocknumber++;
 
204
 
 
205
          // More blocks
 
206
          if (blocknumber < blockcount)
 
207
          {
 
208
            need = blocksize;
 
209
 
 
210
            blockcontext.Reset();
 
211
            blockcrc = 0;
 
212
          }
 
213
        }
 
214
      }
 
215
 
 
216
      if (noiselevel > CommandLine::nlQuiet)
 
217
      {
 
218
        // Display progress
 
219
        u32 oldfraction = (u32)(1000 * offset / filesize);
 
220
        offset += want;
 
221
        u32 newfraction = (u32)(1000 * offset / filesize);
 
222
        if (oldfraction != newfraction)
 
223
        {
 
224
          cout << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
 
225
        }
 
226
      }
 
227
    }
 
228
 
 
229
    // Did we finish the last block
 
230
    if (need > 0)
 
231
    {
 
232
      blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, (size_t)need);
 
233
      blockcontext.Update((size_t)need);
 
234
 
 
235
      MD5Hash blockhash;
 
236
      blockcontext.Final(blockhash);
 
237
 
 
238
      // Store the block hash and block crc in the file verification packet.
 
239
      verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
 
240
 
 
241
      blocknumber++;
 
242
 
 
243
      need = 0;
 
244
    }
 
245
 
 
246
    // Finish computing the file hash.
 
247
    MD5Hash filehash;
 
248
    filecontext.Final(filehash);
 
249
 
 
250
    // Store the file hash in the file description packet.
 
251
    descriptionpacket->HashFull(filehash);
 
252
 
 
253
    // Did we compute the 16k hash.
 
254
    if (offset < 16384)
 
255
    {
 
256
      // Store the 16k hash in the file description packet.
 
257
      descriptionpacket->Hash16k(filehash);
 
258
    }
 
259
 
 
260
    delete [] buffer;
 
261
 
 
262
    // Compute the fileid and store it in the verification packet.
 
263
    descriptionpacket->ComputeFileId();
 
264
    verificationpacket->FileId(descriptionpacket->FileId());
 
265
  }
 
266
 
 
267
  return true;
 
268
}
 
269
 
 
270
void Par2CreatorSourceFile::Close(void)
 
271
{
 
272
  diskfile->Close();
 
273
}
 
274
 
 
275
 
 
276
void Par2CreatorSourceFile::RecordCriticalPackets(list<CriticalPacket*> &criticalpackets)
 
277
{
 
278
  // Add the file description packet and file verification packet to
 
279
  // the critical packet list.
 
280
  criticalpackets.push_back(descriptionpacket);
 
281
  criticalpackets.push_back(verificationpacket);
 
282
}
 
283
 
 
284
bool Par2CreatorSourceFile::CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right)
 
285
{
 
286
  // Sort source files based on fileid
 
287
  return left->descriptionpacket->FileId() < right->descriptionpacket->FileId();
 
288
}
 
289
 
 
290
const MD5Hash& Par2CreatorSourceFile::FileId(void) const
 
291
{
 
292
  // Get the file id hash
 
293
  return descriptionpacket->FileId();
 
294
}
 
295
 
 
296
void Par2CreatorSourceFile::InitialiseSourceBlocks(vector<DataBlock>::iterator &sourceblock, u64 blocksize)
 
297
{
 
298
  for (u32 blocknum=0; blocknum<blockcount; blocknum++)
 
299
  {
 
300
    // Configure each source block to an appropriate offset and length within the source file.
 
301
    sourceblock->SetLocation(diskfile,                                       // file
 
302
                             blocknum * blocksize);                          // offset
 
303
    sourceblock->SetLength(min(blocksize, filesize - (u64)blocknum * blocksize)); // length
 
304
    sourceblock++;
 
305
  }
 
306
}
 
307
 
 
308
void Par2CreatorSourceFile::UpdateHashes(u32 blocknumber, const void *buffer, size_t length)
 
309
{
 
310
  // Compute the crc and hash of the data
 
311
  u32 blockcrc = ~0 ^ CRCUpdateBlock(~0, length, buffer);
 
312
  MD5Context blockcontext;
 
313
  blockcontext.Update(buffer, length);
 
314
  MD5Hash blockhash;
 
315
  blockcontext.Final(blockhash);
 
316
 
 
317
  // Store the results in the verification packet
 
318
  verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
 
319
 
 
320
 
 
321
  // Update the full file hash, but don't go beyond the end of the file
 
322
  if (length > filesize - blocknumber * length)
 
323
  {
 
324
    length = (size_t)(filesize - blocknumber * (u64)length);
 
325
  }
 
326
 
 
327
  assert(contextfull != 0);
 
328
 
 
329
  contextfull->Update(buffer, length);
 
330
}
 
331
 
 
332
void Par2CreatorSourceFile::FinishHashes(void)
 
333
{
 
334
  assert(contextfull != 0);
 
335
 
 
336
  // Finish computation of the full file hash
 
337
  MD5Hash hash;
 
338
  contextfull->Final(hash);
 
339
 
 
340
  // Store it in the description packet
 
341
  descriptionpacket->HashFull(hash);
 
342
}