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 //  Copyright (c) 2019 Michael D. Nahas
6 //
7 //  par2cmdline is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License as published by
9 //  the Free Software Foundation; either version 2 of the License, or
10 //  (at your option) any later version.
11 //
12 //  par2cmdline is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 #include "libpar2internal.h"
22 
23 #ifdef _MSC_VER
24 #ifdef _DEBUG
25 #undef THIS_FILE
26 static char THIS_FILE[]=__FILE__;
27 #define new DEBUG_NEW
28 #endif
29 #endif
30 
31 
32 // static variable
33 #ifdef _OPENMP
34 u32 Par2Creator::filethreads = _FILE_THREADS;
35 #endif
36 
37 
Par2Creator(std::ostream & sout,std::ostream & serr,const NoiseLevel noiselevel)38 Par2Creator::Par2Creator(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel)
39 : sout(sout)
40 , serr(serr)
41 , noiselevel(noiselevel)
42 , blocksize(0)
43 , chunksize(0)
44 , inputbuffer(0)
45 , outputbuffer(0)
46 
47 , sourcefilecount(0)
48 , sourceblockcount(0)
49 
50 , largestfilesize(0)
51 , recoveryfilescheme(scUnknown)
52 , recoveryfilecount(0)
53 , recoveryblockcount(0)
54 , firstrecoveryblock(0)
55 
56 , mainpacket(0)
57 , creatorpacket(0)
58 
59 , sourcefiles()
60 , sourceblocks()
61 , recoveryfiles()
62 , recoverypackets()
63 , criticalpackets()
64 , criticalpacketentries()
65 , rs()
66 , progress(0)
67 , totaldata(0)
68 
69 , deferhashcomputation(false)
70 #ifdef _OPENMP
71 , mttotalsize(0)
72 #endif
73 {
74 }
75 
~Par2Creator(void)76 Par2Creator::~Par2Creator(void)
77 {
78   delete mainpacket;
79   delete creatorpacket;
80 
81   delete [] (u8*)inputbuffer;
82   delete [] (u8*)outputbuffer;
83 
84   vector<Par2CreatorSourceFile*>::iterator sourcefile = sourcefiles.begin();
85   while (sourcefile != sourcefiles.end())
86   {
87     delete *sourcefile;
88     ++sourcefile;
89   }
90 }
91 
Process(const size_t memorylimit,const string & basepath,const u32 nthreads,const u32 _filethreads,const string & parfilename,const vector<string> & _extrafiles,const u64 _blocksize,const u32 _firstblock,const Scheme _recoveryfilescheme,const u32 _recoveryfilecount,const u32 _recoveryblockcount)92 Result Par2Creator::Process(
93 			    const size_t memorylimit,
94 			    const string &basepath,
95 #ifdef _OPENMP
96 			    const u32 nthreads,
97 			    const u32 _filethreads,
98 #endif
99 			    const string &parfilename,
100 			    const vector<string> &_extrafiles,
101 			    const u64 _blocksize,
102 			    const u32 _firstblock,
103 			    const Scheme _recoveryfilescheme,
104 			    const u32 _recoveryfilecount,
105 			    const u32 _recoveryblockcount)
106 {
107 #ifdef _OPENMP
108   filethreads = _filethreads;
109 #endif
110 
111   // Get information from commandline
112   blocksize = _blocksize;
113   const vector<string> extrafiles = _extrafiles;
114   sourcefilecount = (u32)extrafiles.size();
115   recoveryblockcount = _recoveryblockcount;
116   recoveryfilecount = _recoveryfilecount;
117   firstrecoveryblock = _firstblock;
118   recoveryfilescheme = _recoveryfilescheme;
119 
120 #ifdef _OPENMP
121   // Set the number of threads
122   if (nthreads != 0)
123     omp_set_num_threads(nthreads);
124 #endif
125 
126   // Compute block size from block count or vice versa depending on which was
127   // specified on the command line
128   if (!ComputeBlockCount(extrafiles))
129     return eInvalidCommandLineArguments;
130 
131   // Determine how many recovery files to create.
132   if (!ComputeRecoveryFileCount(sout,
133 				serr,
134 				&recoveryfilecount,
135 				recoveryfilescheme,
136 				recoveryblockcount,
137 				largestfilesize,
138 				blocksize)) {
139     return eInvalidCommandLineArguments;
140   }
141 
142   // Determine how much recovery data can be computed on one pass
143   if (!CalculateProcessBlockSize(memorylimit))
144     return eLogicError;
145 
146   if (noiselevel > nlQuiet)
147   {
148     // Display information.
149     sout << "Block size: " << blocksize << endl;
150     sout << "Source file count: " << sourcefilecount << endl;
151     sout << "Source block count: " << sourceblockcount << endl;
152     sout << "Recovery block count: " << recoveryblockcount << endl;
153     sout << "Recovery file count: " << recoveryfilecount << endl;
154     sout << endl;
155   }
156 
157   // Open all of the source files, compute the Hashes and CRC values, and store
158   // the results in the file verification and file description packets.
159   if (!OpenSourceFiles(extrafiles, basepath))
160     return eFileIOError;
161 
162   // Create the main packet and determine the setid to use with all packets
163   if (!CreateMainPacket())
164     return eLogicError;
165 
166   // Create the creator packet.
167   if (!CreateCreatorPacket())
168     return eLogicError;
169 
170   // Initialise all of the source blocks ready to start reading data from the source files.
171   if (!CreateSourceBlocks())
172     return eLogicError;
173 
174   // Create all of the output files and allocate all packets to appropriate file offets.
175   if (!InitialiseOutputFiles(parfilename))
176     return eFileIOError;
177 
178   if (recoveryblockcount > 0)
179   {
180     // Allocate memory buffers for reading and writing data to disk.
181     if (!AllocateBuffers())
182       return eMemoryError;
183 
184     // Compute the Reed Solomon matrix
185     if (!ComputeRSMatrix())
186       return eLogicError;
187 
188     // Set the total amount of data to be processed.
189     progress = 0;
190     totaldata = blocksize * sourceblockcount * recoveryblockcount;
191 
192     // Start at an offset of 0 within a block.
193     u64 blockoffset = 0;
194     while (blockoffset < blocksize) // Continue until the end of the block.
195     {
196       // Work out how much data to process this time.
197       size_t blocklength = (size_t)min((u64)chunksize, blocksize-blockoffset);
198 
199       // Read source data, process it through the RS matrix and write it to disk.
200       if (!ProcessData(blockoffset, blocklength))
201         return eFileIOError;
202 
203       blockoffset += blocklength;
204     }
205 
206     if (noiselevel > nlQuiet)
207       sout << "Writing recovery packets" << endl;
208 
209     // Finish computation of the recovery packets and write the headers to disk.
210     if (!WriteRecoveryPacketHeaders())
211       return eFileIOError;
212 
213     // Finish computing the full file hash values of the source files
214     if (!FinishFileHashComputation())
215       return eLogicError;
216   }
217 
218   // Fill in all remaining details in the critical packets.
219   if (!FinishCriticalPackets())
220     return eLogicError;
221 
222   if (noiselevel > nlQuiet)
223     sout << "Writing verification packets" << endl;
224 
225   // Write all other critical packets to disk.
226   if (!WriteCriticalPackets())
227     return eFileIOError;
228 
229   // Close all files.
230   if (!CloseFiles())
231     return eFileIOError;
232 
233   if (noiselevel > nlSilent)
234     sout << "Done" << endl;
235 
236   return eSuccess;
237 }
238 
239 // Compute block size from block count or vice versa depending on which was
240 // specified on the command line
ComputeBlockCount(const vector<string> & extrafiles)241 bool Par2Creator::ComputeBlockCount(const vector<string> &extrafiles)
242 {
243   FileSizeCache filesize_cache;
244 
245   largestfilesize = 0;
246   for (vector<string>::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++)
247   {
248     u64 filesize = filesize_cache.get(*i);
249     if (largestfilesize < filesize)
250     {
251       largestfilesize = filesize;
252     }
253   }
254 
255 
256   if (blocksize == 0)
257   {
258     serr << "ERROR: Block size was zero!" << endl;
259     return false;
260   }
261 
262   if (blocksize % 4 != 0)
263   {
264     serr << "ERROR: Block size was not a multiple of 4 bytes!" << endl;
265     return false;
266   }
267 
268 
269   u64 count = 0;
270 
271   for (vector<string>::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++)
272   {
273     count += (filesize_cache.get(*i) + blocksize-1) / blocksize;
274   }
275 
276   if (count > 32768)
277   {
278     serr << "Block size is too small. It would require " << count << "blocks." << endl;
279     return false;
280   }
281 
282   sourceblockcount = (u32)count;
283 
284   return true;
285 }
286 
287 
288 
289 // Determine how much recovery data can be computed on one pass
CalculateProcessBlockSize(size_t memorylimit)290 bool Par2Creator::CalculateProcessBlockSize(size_t memorylimit)
291 {
292   // Are we computing any recovery blocks
293   if (recoveryblockcount == 0)
294   {
295     chunksize = 0;
296 
297     deferhashcomputation = false;
298   }
299   else
300   {
301     // Would single pass processing use too much memory
302     if (blocksize * recoveryblockcount > memorylimit)
303     {
304       // Pick a size that is small enough
305       chunksize = ~3 & (memorylimit / recoveryblockcount);
306 
307       deferhashcomputation = false;
308     }
309     else
310     {
311       chunksize = (size_t)blocksize;
312 
313       deferhashcomputation = true;
314     }
315   }
316 
317   return true;
318 }
319 
320 
321 // Open all of the source files, compute the Hashes and CRC values, and store
322 // the results in the file verification and file description packets.
OpenSourceFiles(const vector<string> & extrafiles,string basepath)323 bool Par2Creator::OpenSourceFiles(const vector<string> &extrafiles, string basepath)
324 {
325 #ifdef _OPENMP
326   bool openfailed = false;
327   u64 totalprogress = 0;
328 
329   //Total size of files for mt-progress line
330   for (size_t i=0; i<extrafiles.size(); ++i)
331     mttotalsize += DiskFile::GetFileSize(extrafiles[i]);
332 #endif
333 
334   #pragma omp parallel for schedule(dynamic) num_threads(Par2Creator::GetFileThreads())
335   for (int i=0; i< static_cast<int>(extrafiles.size()); ++i)
336   {
337 #ifdef _OPENMP
338     if (openfailed)
339       continue;
340 #endif
341 
342     Par2CreatorSourceFile *sourcefile = new Par2CreatorSourceFile;
343 
344     string name;
345     DiskFile::SplitRelativeFilename(extrafiles[i], basepath, name);
346 
347     if (noiselevel > nlSilent)
348     {
349       #pragma omp critical
350       sout << "Opening: " << name << endl;
351     }
352 
353     // Open the source file and compute its Hashes and CRCs.
354 #ifdef _OPENMP
355     if (!sourcefile->Open(noiselevel, sout, serr, extrafiles[i], blocksize, deferhashcomputation, basepath, mttotalsize, totalprogress))
356 #else
357     if (!sourcefile->Open(noiselevel, sout, serr, extrafiles[i], blocksize, deferhashcomputation, basepath))
358 #endif
359     {
360       delete sourcefile;
361 #ifdef _OPENMP
362       openfailed = true;
363       continue;
364 #else
365       return false;
366 #endif
367     }
368 
369     // Record the file verification and file description packets
370     // in the critical packet list.
371     #pragma omp critical
372     {
373     sourcefile->RecordCriticalPackets(criticalpackets);
374 
375     // Add the source file to the sourcefiles array.
376     sourcefiles.push_back(sourcefile);
377     }
378     // Close the source file until its needed
379     sourcefile->Close();
380 
381   }
382 
383 #ifdef _OPENMP
384   if (openfailed)
385     return false;
386 #endif
387 
388   return true;
389 }
390 
391 // Create the main packet and determine the setid to use with all packets
CreateMainPacket(void)392 bool Par2Creator::CreateMainPacket(void)
393 {
394   // Construct the main packet from the list of source files and the block size.
395   mainpacket = new MainPacket;
396 
397   // Add the main packet to the list of critical packets.
398   criticalpackets.push_back(mainpacket);
399 
400   // Create the packet (sourcefiles will get sorted into FileId order).
401   return mainpacket->Create(sourcefiles, blocksize);
402 }
403 
404 // Create the creator packet.
CreateCreatorPacket(void)405 bool Par2Creator::CreateCreatorPacket(void)
406 {
407   // Construct the creator packet
408   creatorpacket = new CreatorPacket;
409 
410   // Create the packet
411   return creatorpacket->Create(mainpacket->SetId());
412 }
413 
414 // Initialise all of the source blocks ready to start reading data from the source files.
CreateSourceBlocks(void)415 bool Par2Creator::CreateSourceBlocks(void)
416 {
417   // Allocate the array of source blocks
418   sourceblocks.resize(sourceblockcount);
419 
420   vector<DataBlock>::iterator sourceblock = sourceblocks.begin();
421 
422   for (vector<Par2CreatorSourceFile*>::iterator sourcefile = sourcefiles.begin();
423        sourcefile!= sourcefiles.end();
424        sourcefile++)
425   {
426     // Allocate the appopriate number of source blocks to each source file.
427     // sourceblock will be advanced.
428 
429     (*sourcefile)->InitialiseSourceBlocks(sourceblock, blocksize);
430   }
431 
432   return true;
433 }
434 
435 class FileAllocation
436 {
437 public:
FileAllocation(void)438   FileAllocation(void)
439   : filename("")
440   {
441     exponent = 0;
442     count = 0;
443   }
444 
445   string filename;
446   u32 exponent;
447   u32 count;
448 };
449 
450 // Create all of the output files and allocate all packets to appropriate file offets.
InitialiseOutputFiles(const string & parfilename)451 bool Par2Creator::InitialiseOutputFiles(const string &parfilename)
452 {
453   // Allocate the recovery packets
454   recoverypackets.resize(recoveryblockcount);
455 
456   // Choose filenames and decide which recovery blocks to place in each file
457   vector<FileAllocation> fileallocations;
458   fileallocations.resize(recoveryfilecount+1); // One extra file with no recovery blocks
459   {
460     // Decide how many recovery blocks to place in each file
461     u32 exponent = firstrecoveryblock;
462     if (recoveryfilecount > 0)
463     {
464       switch (recoveryfilescheme)
465       {
466       case scUnknown:
467         {
468           assert(false);
469           return false;
470         }
471         break;
472       case scUniform:
473         {
474           // Files will have roughly the same number of recovery blocks each.
475 
476           u32 base      = recoveryblockcount / recoveryfilecount;
477           u32 remainder = recoveryblockcount % recoveryfilecount;
478 
479           for (u32 filenumber=0; filenumber<recoveryfilecount; filenumber++)
480           {
481             fileallocations[filenumber].exponent = exponent;
482             fileallocations[filenumber].count = (filenumber<remainder) ? base+1 : base;
483             exponent += fileallocations[filenumber].count;
484           }
485         }
486         break;
487 
488       case scVariable:
489         {
490           // Files will have recovery blocks allocated in an exponential fashion.
491 
492           // Work out how many blocks to place in the smallest file
493           u32 lowblockcount = 1;
494           u32 maxrecoveryblocks = (1 << recoveryfilecount) - 1;
495           while (maxrecoveryblocks < recoveryblockcount)
496           {
497             lowblockcount <<= 1;
498             maxrecoveryblocks <<= 1;
499           }
500 
501           // Allocate the blocks.
502           u32 blocks = recoveryblockcount;
503           for (u32 filenumber=0; filenumber<recoveryfilecount; filenumber++)
504           {
505             u32 number = min(lowblockcount, blocks);
506             fileallocations[filenumber].exponent = exponent;
507             fileallocations[filenumber].count = number;
508             exponent += number;
509             blocks -= number;
510             lowblockcount <<= 1;
511           }
512         }
513         break;
514 
515       case scLimited:
516         {
517           // Files will be allocated in an exponential fashion but the
518           // Maximum file size will be limited.
519 
520           u32 largest = (u32)((largestfilesize + blocksize-1) / blocksize);
521           u32 filenumber = recoveryfilecount;
522           u32 blocks = recoveryblockcount;
523 
524           exponent = firstrecoveryblock + recoveryblockcount;
525 
526           // Allocate uniformly at the top
527           while (blocks >= 2*largest && filenumber > 0)
528           {
529             filenumber--;
530             exponent -= largest;
531             blocks -= largest;
532 
533             fileallocations[filenumber].exponent = exponent;
534             fileallocations[filenumber].count = largest;
535           }
536           assert(blocks > 0 && filenumber > 0);
537 
538           exponent = firstrecoveryblock;
539           u32 count = 1;
540           u32 files = filenumber;
541 
542           // Allocate exponentially at the bottom
543           for (filenumber=0; filenumber<files; filenumber++)
544           {
545             u32 number = min(count, blocks);
546             fileallocations[filenumber].exponent = exponent;
547             fileallocations[filenumber].count = number;
548 
549             exponent += number;
550             blocks -= number;
551             count <<= 1;
552           }
553         }
554         break;
555       }
556     }
557 
558      // There will be an extra file with no recovery blocks.
559     fileallocations[recoveryfilecount].exponent = exponent;
560     fileallocations[recoveryfilecount].count = 0;
561 
562     // Determine the format to use for filenames of recovery files
563     char filenameformat[_MAX_PATH];
564     {
565       u32 limitLow = 0;
566       u32 limitCount = 0;
567       for (u32 filenumber=0; filenumber<=recoveryfilecount; filenumber++)
568       {
569         if (limitLow < fileallocations[filenumber].exponent)
570         {
571           limitLow = fileallocations[filenumber].exponent;
572         }
573         if (limitCount < fileallocations[filenumber].count)
574         {
575           limitCount = fileallocations[filenumber].count;
576         }
577       }
578 
579       u32 digitsLow = 1;
580       for (u32 t=limitLow; t>=10; t/=10)
581       {
582         digitsLow++;
583       }
584 
585       u32 digitsCount = 1;
586       for (u32 t=limitCount; t>=10; t/=10)
587       {
588         digitsCount++;
589       }
590 
591       sprintf(filenameformat, "%%s.vol%%0%dd+%%0%dd.par2", (int) digitsLow, (int) digitsCount);
592     }
593 
594     // Set the filenames
595     for (u32 filenumber=0; filenumber<recoveryfilecount; filenumber++)
596     {
597       char filename[_MAX_PATH];
598       snprintf(filename, sizeof(filename), filenameformat, parfilename.c_str(), fileallocations[filenumber].exponent, fileallocations[filenumber].count);
599       fileallocations[filenumber].filename = filename;
600     }
601     fileallocations[recoveryfilecount].filename = parfilename + ".par2";
602   }
603 
604   // Allocate the recovery files
605   {
606     recoveryfiles.resize(recoveryfilecount+1, DiskFile(sout, serr)); // pass default constructor.
607 
608     // Sort critical packets, so we get consistency.
609     criticalpackets.sort(CriticalPacket::CompareLess);
610 
611     // Allocate packets to the output files
612     {
613       const MD5Hash &setid = mainpacket->SetId();
614       vector<RecoveryPacket>::iterator recoverypacket = recoverypackets.begin();
615 
616       vector<DiskFile>::iterator recoveryfile = recoveryfiles.begin();
617       vector<FileAllocation>::iterator fileallocation = fileallocations.begin();
618 
619       // For each recovery file:
620       while (recoveryfile != recoveryfiles.end())
621       {
622         // How many recovery blocks in this file
623         u32 count = fileallocation->count;
624 
625         // start at the beginning of the recovery file
626         u64 offset = 0;
627 
628         if (count == 0)
629         {
630           // Write one set of critical packets
631           list<CriticalPacket*>::const_iterator nextCriticalPacket = criticalpackets.begin();
632 
633           while (nextCriticalPacket != criticalpackets.end())
634           {
635             criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile,
636                                                                 offset,
637                                                                 *nextCriticalPacket));
638             offset += (*nextCriticalPacket)->PacketLength();
639 
640             ++nextCriticalPacket;
641           }
642         }
643         else
644         {
645           // How many copies of each critical packet
646           u32 copies = 0;
647           for (u32 t=count; t>0; t>>=1)
648           {
649             copies++;
650           }
651 
652           // Get ready to iterate through the critical packets
653           u64 packetCount = 0;
654           list<CriticalPacket*>::const_iterator nextCriticalPacket = criticalpackets.end();
655 
656           // What is the first exponent
657           u32 exponent = fileallocation->exponent;
658 
659           // Start allocating the recovery packets
660           u32 limit = exponent + count;
661           while (exponent < limit)
662           {
663             // Add the next recovery packet
664             recoverypacket->Create(&*recoveryfile, offset, blocksize, exponent, setid);
665 
666             offset += recoverypacket->PacketLength();
667             ++recoverypacket;
668             ++exponent;
669 
670             // Add some critical packets
671             packetCount += copies * criticalpackets.size();
672             while (packetCount >= count)
673             {
674               if (nextCriticalPacket == criticalpackets.end()) nextCriticalPacket = criticalpackets.begin();
675               criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile,
676                                                                   offset,
677                                                                   *nextCriticalPacket));
678               offset += (*nextCriticalPacket)->PacketLength();
679               ++nextCriticalPacket;
680 
681               packetCount -= count;
682             }
683           }
684         }
685 
686         // Add one copy of the creator packet
687         criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile,
688                                                             offset,
689                                                             creatorpacket));
690         offset += creatorpacket->PacketLength();
691 
692         // Create the file on disk and make it the required size
693         if (!recoveryfile->Create(fileallocation->filename, offset))
694           return false;
695 
696         ++recoveryfile;
697         ++fileallocation;
698       }
699     }
700   }
701 
702   return true;
703 }
704 
705 // Allocate memory buffers for reading and writing data to disk.
AllocateBuffers(void)706 bool Par2Creator::AllocateBuffers(void)
707 {
708   inputbuffer = new u8[chunksize];
709   outputbuffer = new u8[chunksize * recoveryblockcount];
710 
711   if (inputbuffer == NULL || outputbuffer == NULL)
712   {
713     serr << "Could not allocate buffer memory." << endl;
714     return false;
715   }
716 
717   return true;
718 }
719 
720 // Compute the Reed Solomon matrix
ComputeRSMatrix(void)721 bool Par2Creator::ComputeRSMatrix(void)
722 {
723   // Set the number of input blocks
724   if (!rs.SetInput(sourceblockcount, sout, serr))
725     return false;
726 
727   // Set the number of output blocks to be created
728   if (!rs.SetOutput(false,
729                     (u16)firstrecoveryblock,
730                     (u16)firstrecoveryblock + (u16)(recoveryblockcount-1)))
731     return false;
732 
733   // Compute the RS matrix
734   if (!rs.Compute(noiselevel, sout, serr))
735     return false;
736 
737   return true;
738 }
739 
740 // Read source data, process it through the RS matrix and write it to disk.
ProcessData(u64 blockoffset,size_t blocklength)741 bool Par2Creator::ProcessData(u64 blockoffset, size_t blocklength)
742 {
743   // Clear the output buffer
744   memset(outputbuffer, 0, chunksize * recoveryblockcount);
745 
746   // If we have defered computation of the file hash and block crc and hashes
747   // sourcefile and sourceindex will be used to update them during
748   // the main recovery block computation
749   vector<Par2CreatorSourceFile*>::iterator sourcefile = sourcefiles.begin();
750   u32 sourceindex = 0;
751 
752   vector<DataBlock>::iterator sourceblock;
753   u32 inputblock;
754 
755   DiskFile *lastopenfile = NULL;
756 
757   // For each input block
758   for ((sourceblock=sourceblocks.begin()),(inputblock=0);
759        sourceblock != sourceblocks.end();
760        ++sourceblock, ++inputblock)
761   {
762     // Are we reading from a new file?
763     if (lastopenfile != (*sourceblock).GetDiskFile())
764     {
765       // Close the last file
766       if (lastopenfile != NULL)
767       {
768         lastopenfile->Close();
769       }
770 
771       // Open the new file
772       lastopenfile = (*sourceblock).GetDiskFile();
773       if (!lastopenfile->Open())
774       {
775         return false;
776       }
777     }
778 
779     // Read data from the current input block
780     if (!sourceblock->ReadData(blockoffset, blocklength, inputbuffer))
781       return false;
782 
783     if (deferhashcomputation)
784     {
785       assert(blockoffset == 0 && blocklength == blocksize);
786       assert(sourcefile != sourcefiles.end());
787 
788       (*sourcefile)->UpdateHashes(sourceindex, inputbuffer, blocklength);
789     }
790 
791     // For each output block
792     #pragma omp parallel for
793     for (i64 outputblock=0; outputblock<recoveryblockcount; outputblock++)
794     {
795       u32 internalOutputblock = (u32)outputblock;
796 
797       // Select the appropriate part of the output buffer
798       void *outbuf = &((u8*)outputbuffer)[chunksize * internalOutputblock];
799 
800       // Process the data through the RS matrix
801       rs.Process(blocklength, inputblock, inputbuffer, internalOutputblock, outbuf);
802 
803       if (noiselevel > nlQuiet)
804       {
805         // Update a progress indicator
806         u32 oldfraction = (u32)(1000 * progress / totaldata);
807         #pragma omp atomic
808         progress += blocklength;
809         u32 newfraction = (u32)(1000 * progress / totaldata);
810 
811         if (oldfraction != newfraction)
812         {
813           #pragma omp critical
814           sout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
815         }
816       }
817     }
818 
819     // Work out which source file the next block belongs to
820     if (++sourceindex >= (*sourcefile)->BlockCount())
821     {
822       sourceindex = 0;
823       ++sourcefile;
824     }
825   }
826 
827   // Close the last file
828   if (lastopenfile != NULL)
829   {
830     lastopenfile->Close();
831   }
832 
833   if (noiselevel > nlQuiet)
834     sout << "Writing recovery packets\r";
835 
836   // For each output block
837   for (u32 outputblock=0; outputblock<recoveryblockcount;outputblock++)
838   {
839     // Select the appropriate part of the output buffer
840     char *outbuf = &((char*)outputbuffer)[chunksize * outputblock];
841 
842     // Write the data to the recovery packet
843     if (!recoverypackets[outputblock].WriteData(blockoffset, blocklength, outbuf))
844       return false;
845   }
846 
847   if (noiselevel > nlQuiet)
848     sout << "Wrote " << recoveryblockcount * blocklength << " bytes to disk" << endl;
849 
850   return true;
851 }
852 
853 // Finish computation of the recovery packets and write the headers to disk.
WriteRecoveryPacketHeaders(void)854 bool Par2Creator::WriteRecoveryPacketHeaders(void)
855 {
856   // For each recovery packet
857   for (vector<RecoveryPacket>::iterator recoverypacket = recoverypackets.begin();
858        recoverypacket != recoverypackets.end();
859        ++recoverypacket)
860   {
861     // Finish the packet header and write it to disk
862     if (!recoverypacket->WriteHeader())
863       return false;
864   }
865 
866   return true;
867 }
868 
FinishFileHashComputation(void)869 bool Par2Creator::FinishFileHashComputation(void)
870 {
871   // If we defered the computation of the full file hash, then we finish it now
872   if (deferhashcomputation)
873   {
874     // For each source file
875     vector<Par2CreatorSourceFile*>::iterator sourcefile = sourcefiles.begin();
876 
877     while (sourcefile != sourcefiles.end())
878     {
879       (*sourcefile)->FinishHashes();
880 
881       ++sourcefile;
882     }
883   }
884 
885   return true;
886 }
887 
888 // Fill in all remaining details in the critical packets.
FinishCriticalPackets(void)889 bool Par2Creator::FinishCriticalPackets(void)
890 {
891   // Get the setid from the main packet
892   const MD5Hash &setid = mainpacket->SetId();
893 
894   for (list<CriticalPacket*>::iterator criticalpacket=criticalpackets.begin();
895        criticalpacket!=criticalpackets.end();
896        criticalpacket++)
897   {
898     // Store the setid in each of the critical packets
899     // and compute the packet_hash of each one.
900 
901     (*criticalpacket)->FinishPacket(setid);
902   }
903 
904   return true;
905 }
906 
907 // Write all other critical packets to disk.
WriteCriticalPackets(void)908 bool Par2Creator::WriteCriticalPackets(void)
909 {
910   list<CriticalPacketEntry>::const_iterator packetentry = criticalpacketentries.begin();
911 
912   // For each critical packet
913   while (packetentry != criticalpacketentries.end())
914   {
915     // Write it to disk
916     if (!packetentry->WritePacket())
917       return false;
918 
919     ++packetentry;
920   }
921 
922   return true;
923 }
924 
925 // Close all files.
CloseFiles(void)926 bool Par2Creator::CloseFiles(void)
927 {
928 //  // Close each source file.
929 //  for (vector<Par2CreatorSourceFile*>::iterator sourcefile = sourcefiles.begin();
930 //       sourcefile != sourcefiles.end();
931 //       ++sourcefile)
932 //  {
933 //    (*sourcefile)->Close();
934 //  }
935 
936   // Close each recovery file.
937   for (vector<DiskFile>::iterator recoveryfile = recoveryfiles.begin();
938        recoveryfile != recoveryfiles.end();
939        ++recoveryfile)
940   {
941     recoveryfile->Close();
942   }
943 
944   return true;
945 }
946