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