1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4
5 #include <IceUtil/StringUtil.h>
6 #include <IceUtil/FileUtil.h>
7 #include <IcePatch2/ClientUtil.h>
8 #include <IcePatch2Lib/Util.h>
9 #include <list>
10 #include <iterator>
11
12 using namespace std;
13 using namespace Ice;
14 using namespace IceUtil;
15 using namespace IcePatch2;
16 using namespace IcePatch2Internal;
17
~Patcher()18 IcePatch2::Patcher::~Patcher()
19 {
20 // Out of line to avoid weak vtable
21 }
22
~PatcherFeedback()23 IcePatch2::PatcherFeedback::~PatcherFeedback()
24 {
25 // Out of line to avoid weak vtable
26 }
27
28 namespace
29 {
30
31 class Decompressor : public IceUtil::Thread, public IceUtil::Monitor<IceUtil::Mutex>
32 {
33 public:
34
35 Decompressor(const string&);
36 virtual ~Decompressor();
37
38 void destroy();
39 void add(const LargeFileInfo&);
40 void exception() const;
41 void log(FILE* fp);
42 virtual void run();
43
44 private:
45
46 const string _dataDir;
47
48 string _exception;
49 list<LargeFileInfo> _files;
50 LargeFileInfoSeq _filesDone;
51 bool _destroy;
52 };
53 typedef IceUtil::Handle<Decompressor> DecompressorPtr;
54
55 class PatcherI : public Patcher
56 {
57 public:
58
59 PatcherI(const Ice::CommunicatorPtr&, const PatcherFeedbackPtr&);
60 PatcherI(const FileServerPrx&, const PatcherFeedbackPtr&, const std::string&, bool, Ice::Int, Ice::Int);
61 virtual ~PatcherI();
62
63 virtual bool prepare();
64 virtual bool patch(const std::string&);
65 virtual void finish();
66
67 private:
68
69 void init(const FileServerPrx&);
70 bool removeFiles(const LargeFileInfoSeq&);
71 bool updateFiles(const LargeFileInfoSeq&);
72 bool updateFilesInternal(const LargeFileInfoSeq&, const DecompressorPtr&);
73 bool updateFlags(const LargeFileInfoSeq&);
74
75 const PatcherFeedbackPtr _feedback;
76 const std::string _dataDir;
77 const bool _thorough;
78 const Ice::Int _chunkSize;
79 const Ice::Int _remove;
80 const FileServerPrx _serverCompress;
81 const FileServerPrx _serverNoCompress;
82
83 LargeFileInfoSeq _localFiles;
84 LargeFileInfoSeq _updateFiles;
85 LargeFileInfoSeq _updateFlags;
86 LargeFileInfoSeq _removeFiles;
87
88 FILE* _log;
89 bool _useSmallFileAPI;
90 };
91
Decompressor(const string & dataDir)92 Decompressor::Decompressor(const string& dataDir) :
93 _dataDir(dataDir),
94 _destroy(false)
95 {
96 }
97
~Decompressor()98 Decompressor::~Decompressor()
99 {
100 assert(_destroy);
101 }
102
103 void
destroy()104 Decompressor::destroy()
105 {
106 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
107 _destroy = true;
108 notify();
109 }
110
111 void
add(const LargeFileInfo & info)112 Decompressor::add(const LargeFileInfo& info)
113 {
114 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
115 if(!_exception.empty())
116 {
117 throw runtime_error(_exception);
118 }
119 _files.push_back(info);
120 notify();
121 }
122
123 void
exception() const124 Decompressor::exception() const
125 {
126 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
127 if(!_exception.empty())
128 {
129 throw runtime_error(_exception);
130 }
131 }
132
133 void
log(FILE * fp)134 Decompressor::log(FILE* fp)
135 {
136 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
137
138 for(LargeFileInfoSeq::const_iterator p = _filesDone.begin(); p != _filesDone.end(); ++p)
139 {
140 if(fputc('+', fp) == EOF || !writeFileInfo(fp, *p))
141 {
142 throw runtime_error("error writing log file:\n" + IceUtilInternal::lastErrorToString());
143 }
144 }
145
146 _filesDone.clear();
147 }
148
149 void
run()150 Decompressor::run()
151 {
152 LargeFileInfo info;
153
154 while(true)
155 {
156 {
157 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
158
159 if(!info.path.empty())
160 {
161 _filesDone.push_back(info);
162 }
163
164 while(!_destroy && _files.empty())
165 {
166 wait();
167 }
168
169 if(!_files.empty())
170 {
171 info = _files.front();
172 _files.pop_front();
173 }
174 else
175 {
176 return;
177 }
178 }
179
180 try
181 {
182 decompressFile(_dataDir + '/' + info.path);
183 setFileFlags(_dataDir + '/' + info.path, info);
184 remove(_dataDir + '/' + info.path + ".bz2");
185 }
186 catch(const std::exception& ex)
187 {
188 IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this);
189 _destroy = true;
190 _exception = ex.what();
191 return;
192 }
193 }
194 }
195
PatcherI(const CommunicatorPtr & communicator,const PatcherFeedbackPtr & feedback)196 PatcherI::PatcherI(const CommunicatorPtr& communicator, const PatcherFeedbackPtr& feedback) :
197 _feedback(feedback),
198 _dataDir(communicator->getProperties()->getPropertyWithDefault("IcePatch2Client.Directory", ".")),
199 _thorough(communicator->getProperties()->getPropertyAsIntWithDefault("IcePatch2Client.Thorough", 0) > 0),
200 _chunkSize(communicator->getProperties()->getPropertyAsIntWithDefault("IcePatch2Client.ChunkSize", 100)),
201 _remove(communicator->getProperties()->getPropertyAsIntWithDefault("IcePatch2Client.Remove", 1)),
202 _log(0),
203 _useSmallFileAPI(false)
204 {
205 const char* clientProxyProperty = "IcePatch2Client.Proxy";
206 string clientProxy = communicator->getProperties()->getProperty(clientProxyProperty);
207 if(clientProxy.empty())
208 {
209 throw runtime_error("property `IcePatch2Client.Proxy' is not set");
210 }
211
212 FileServerPrx server = FileServerPrx::checkedCast(communicator->stringToProxy(clientProxy));
213 if(!server)
214 {
215 throw runtime_error("proxy `" + clientProxy + "' is not a file server.");
216 }
217
218 init(server);
219 }
220
PatcherI(const FileServerPrx & server,const PatcherFeedbackPtr & feedback,const string & dataDir,bool thorough,Ice::Int chunkSize,Ice::Int remove)221 PatcherI::PatcherI(const FileServerPrx& server,
222 const PatcherFeedbackPtr& feedback,
223 const string& dataDir,
224 bool thorough,
225 Ice::Int chunkSize,
226 Ice::Int remove) :
227 _feedback(feedback),
228 _dataDir(dataDir),
229 _thorough(thorough),
230 _chunkSize(chunkSize),
231 _remove(remove),
232 _useSmallFileAPI(false)
233 {
234 init(server);
235 }
236
~PatcherI()237 PatcherI::~PatcherI()
238 {
239 if(_log != 0)
240 {
241 fclose(_log);
242 _log = 0;
243 }
244 }
245
246 class PatcherGetFileInfoSeqCB : public GetFileInfoSeqCB
247 {
248 public:
249
PatcherGetFileInfoSeqCB(const PatcherFeedbackPtr & feedback)250 PatcherGetFileInfoSeqCB(const PatcherFeedbackPtr& feedback) :
251 _feedback(feedback)
252 {
253 }
254
255 virtual bool
remove(const string &)256 remove(const string&)
257 {
258 return true;
259 }
260
261 virtual bool
checksum(const string & path)262 checksum(const string& path)
263 {
264 return _feedback->checksumProgress(path);
265 }
266
compress(const string &)267 virtual bool compress(const string&)
268 {
269 assert(false); // Nothing must get compressed when we are patching.
270 return true;
271 }
272
273 private:
274
275 const PatcherFeedbackPtr _feedback;
276 };
277
278 bool
prepare()279 PatcherI::prepare()
280 {
281 _localFiles.clear();
282
283 bool thorough = _thorough;
284
285 if(!thorough)
286 {
287 try
288 {
289 loadFileInfoSeq(_dataDir, _localFiles);
290 }
291 catch(const exception& ex)
292 {
293 thorough = _feedback->noFileSummary(ex.what());
294 if(!thorough)
295 {
296 return false;
297 }
298 }
299 }
300
301 if(thorough)
302 {
303 if(!_feedback->checksumStart())
304 {
305 return false;
306 }
307
308 PatcherGetFileInfoSeqCB cb(_feedback);
309 if(!getFileInfoSeq(_dataDir, 0, &cb, _localFiles))
310 {
311 return false;
312 }
313
314 if(!_feedback->checksumEnd())
315 {
316 return false;
317 }
318
319 saveFileInfoSeq(_dataDir, _localFiles);
320 }
321
322 FileTree0 tree0;
323 getFileTree0(_localFiles, tree0);
324
325 if(tree0.checksum != _serverCompress->getChecksum())
326 {
327 if(!_feedback->fileListStart())
328 {
329 return false;
330 }
331
332 ByteSeqSeq checksumSeq = _serverCompress->getChecksumSeq();
333 if(checksumSeq.size() != 256)
334 {
335 throw runtime_error("server returned illegal value");
336 }
337
338 while(true)
339 {
340 AsyncResultPtr curCB;
341 AsyncResultPtr nxtCB;
342 try
343 {
344 for(int node0 = 0; node0 < 256; ++node0)
345 {
346 if(tree0.nodes[node0].checksum != checksumSeq[node0])
347 {
348 if(!curCB)
349 {
350 assert(!nxtCB);
351 curCB = _useSmallFileAPI ? _serverCompress->begin_getFileInfoSeq(node0) :
352 _serverCompress->begin_getLargeFileInfoSeq(node0);
353 }
354 else
355 {
356 assert(nxtCB);
357 swap(nxtCB, curCB);
358 }
359
360 int node0Nxt = node0;
361
362 do
363 {
364 ++node0Nxt;
365 }
366 while(node0Nxt < 256 && tree0.nodes[node0Nxt].checksum == checksumSeq[node0Nxt]);
367
368 if(node0Nxt < 256)
369 {
370 nxtCB = _useSmallFileAPI ? _serverCompress->begin_getFileInfoSeq(node0Nxt) :
371 _serverCompress->begin_getLargeFileInfoSeq(node0Nxt);
372 }
373
374 LargeFileInfoSeq files;
375 if(_useSmallFileAPI)
376 {
377 FileInfoSeq smallFiles = _serverCompress->end_getFileInfoSeq(curCB);
378 files.resize(smallFiles.size());
379 transform(smallFiles.begin(), smallFiles.end(), files.begin(), toLargeFileInfo);
380 }
381 else
382 {
383 files = _serverCompress->end_getLargeFileInfoSeq(curCB);
384 }
385
386 sort(files.begin(), files.end(), FileInfoLess());
387 files.erase(unique(files.begin(), files.end(), FileInfoEqual()), files.end());
388
389 //
390 // Compute the set of files which were removed.
391 //
392 set_difference(tree0.nodes[node0].files.begin(),
393 tree0.nodes[node0].files.end(),
394 files.begin(),
395 files.end(),
396 back_inserter(_removeFiles),
397 FileInfoWithoutFlagsLess()); // NOTE: We ignore the flags here.
398
399 //
400 // Compute the set of files which were updated (either the file contents, flags or both).
401 //
402 LargeFileInfoSeq updatedFiles;
403 updatedFiles.reserve(files.size());
404
405 set_difference(files.begin(),
406 files.end(),
407 tree0.nodes[node0].files.begin(),
408 tree0.nodes[node0].files.end(),
409 back_inserter(updatedFiles),
410 FileInfoLess());
411
412 //
413 // Compute the set of files whose contents was updated.
414 //
415 LargeFileInfoSeq contentsUpdatedFiles;
416 contentsUpdatedFiles.reserve(files.size());
417
418 set_difference(files.begin(),
419 files.end(),
420 tree0.nodes[node0].files.begin(),
421 tree0.nodes[node0].files.end(),
422 back_inserter(contentsUpdatedFiles),
423 FileInfoWithoutFlagsLess()); // NOTE: We ignore the flags here.
424 copy(contentsUpdatedFiles.begin(), contentsUpdatedFiles.end(), back_inserter(_updateFiles));
425
426 //
427 // Compute the set of files whose flags were updated.
428 //
429 set_difference(updatedFiles.begin(),
430 updatedFiles.end(),
431 contentsUpdatedFiles.begin(),
432 contentsUpdatedFiles.end(),
433 back_inserter(_updateFlags),
434 FileInfoLess());
435 }
436
437 if(!_feedback->fileListProgress((node0 + 1) * 100 / 256))
438 {
439 return false;
440 }
441 }
442 }
443 catch(const Ice::OperationNotExistException&)
444 {
445 if(!_useSmallFileAPI)
446 {
447 _useSmallFileAPI = true;
448 continue;
449 }
450 throw;
451 }
452 break;
453 }
454
455 if(!_feedback->fileListEnd())
456 {
457 return false;
458 }
459 }
460
461 sort(_removeFiles.begin(), _removeFiles.end(), FileInfoLess());
462 sort(_updateFiles.begin(), _updateFiles.end(), FileInfoLess());
463 sort(_updateFlags.begin(), _updateFlags.end(), FileInfoLess());
464
465 string pathLog = simplify(_dataDir + '/' + logFile);
466 _log = IceUtilInternal::fopen(pathLog, "w");
467 if(!_log)
468 {
469 throw runtime_error("cannot open `" + pathLog + "' for writing:\n" + IceUtilInternal::lastErrorToString());
470 }
471
472 return true;
473 }
474
475 bool
patch(const string & d)476 PatcherI::patch(const string& d)
477 {
478 string dir = simplify(d);
479
480 if(dir.empty() || dir == ".")
481 {
482 if(!_removeFiles.empty())
483 {
484 if(!removeFiles(_removeFiles))
485 {
486 return false;
487 }
488 }
489
490 if(!_updateFiles.empty())
491 {
492 if(!updateFiles(_updateFiles))
493 {
494 return false;
495 }
496 }
497
498 if(!_updateFlags.empty())
499 {
500 if(!updateFlags(_updateFlags))
501 {
502 return false;
503 }
504 }
505
506 return true;
507 }
508 else
509 {
510 string dirWithSlash = simplify(dir + '/');
511
512 LargeFileInfoSeq remove;
513 for(LargeFileInfoSeq::const_iterator p = _removeFiles.begin(); p != _removeFiles.end(); ++p)
514 {
515 if(p->path == dir)
516 {
517 remove.push_back(*p);
518 }
519 else if(p->path.compare(0, dirWithSlash.size(), dirWithSlash) == 0)
520 {
521 remove.push_back(*p);
522 }
523 }
524
525 LargeFileInfoSeq update;
526 for(LargeFileInfoSeq::const_iterator p = _updateFiles.begin(); p != _updateFiles.end(); ++p)
527 {
528 if(p->path == dir)
529 {
530 update.push_back(*p);
531 }
532 else if(p->path.compare(0, dirWithSlash.size(), dirWithSlash) == 0)
533 {
534 update.push_back(*p);
535 }
536 }
537
538 LargeFileInfoSeq updateFlag;
539 for(LargeFileInfoSeq::const_iterator p = _updateFlags.begin(); p != _updateFlags.end(); ++p)
540 {
541 if(p->path == dir)
542 {
543 updateFlag.push_back(*p);
544 }
545 else if(p->path.compare(0, dirWithSlash.size(), dirWithSlash) == 0)
546 {
547 updateFlag.push_back(*p);
548 }
549 }
550
551 if(!remove.empty())
552 {
553 if(!removeFiles(remove))
554 {
555 return false;
556 }
557 }
558
559 if(!update.empty())
560 {
561 if(!updateFiles(update))
562 {
563 return false;
564 }
565 }
566
567 if(!updateFlag.empty())
568 {
569 if(!updateFlags(updateFlag))
570 {
571 return false;
572 }
573 }
574
575 return true;
576 }
577 }
578
579 void
finish()580 PatcherI::finish()
581 {
582 if(_log != 0)
583 {
584 fclose(_log);
585 _log = 0;
586 }
587
588 saveFileInfoSeq(_dataDir, _localFiles);
589 }
590
591 void
init(const FileServerPrx & server)592 PatcherI::init(const FileServerPrx& server)
593 {
594 if(_dataDir.empty())
595 {
596 throw runtime_error("no data directory specified");
597 }
598
599 Ice::CommunicatorPtr communicator = server->ice_getCommunicator();
600
601 const_cast<string&>(_dataDir) = simplify(_dataDir);
602
603 //
604 // Make sure that _chunkSize doesn't exceed MessageSizeMax, otherwise
605 // it won't work at all.
606 //
607 int sizeMax = communicator->getProperties()->getPropertyAsIntWithDefault("Ice.MessageSizeMax", 1024);
608 if(_chunkSize < 1)
609 {
610 const_cast<Int&>(_chunkSize) = 1;
611 }
612 else if(_chunkSize > sizeMax)
613 {
614 const_cast<Int&>(_chunkSize) = sizeMax;
615 }
616 if(_chunkSize == sizeMax)
617 {
618 const_cast<Int&>(_chunkSize) = _chunkSize * 1024 - 512; // Leave some headroom for protocol header.
619 }
620 else
621 {
622 const_cast<Int&>(_chunkSize) *= 1024;
623 }
624
625 if(!IceUtilInternal::isAbsolutePath(_dataDir))
626 {
627 string cwd;
628 if(IceUtilInternal::getcwd(cwd) != 0)
629 {
630 throw runtime_error("cannot get the current directory:\n" + IceUtilInternal::lastErrorToString());
631 }
632 const_cast<string&>(_dataDir) = simplify(cwd + '/' + _dataDir);
633 }
634
635 const_cast<FileServerPrx&>(_serverCompress) = FileServerPrx::uncheckedCast(server->ice_compress(true));
636 const_cast<FileServerPrx&>(_serverNoCompress) = FileServerPrx::uncheckedCast(server->ice_compress(false));
637 }
638
639 bool
removeFiles(const LargeFileInfoSeq & files)640 PatcherI::removeFiles(const LargeFileInfoSeq& files)
641 {
642 if(_remove < 1)
643 {
644 return true;
645 }
646
647 for(LargeFileInfoSeq::const_reverse_iterator p = files.rbegin(); p != files.rend(); ++p)
648 {
649 try
650 {
651 remove(_dataDir + '/' + p->path);
652 if(fputc('-', _log) == EOF || ! writeFileInfo(_log, *p))
653 {
654 throw runtime_error("error writing log file:\n" + IceUtilInternal::lastErrorToString());
655 }
656 }
657 catch(...)
658 {
659 if(_remove < 2) // We ignore errors if IcePatch2Client.Remove >= 2.
660 {
661 throw;
662 }
663 }
664 }
665
666 LargeFileInfoSeq newLocalFiles;
667 newLocalFiles.reserve(_localFiles.size());
668
669 set_difference(_localFiles.begin(),
670 _localFiles.end(),
671 files.begin(),
672 files.end(),
673 back_inserter(newLocalFiles),
674 FileInfoLess());
675
676 _localFiles.swap(newLocalFiles);
677
678 LargeFileInfoSeq newRemoveFiles;
679
680 set_difference(_removeFiles.begin(),
681 _removeFiles.end(),
682 files.begin(),
683 files.end(),
684 back_inserter(newRemoveFiles),
685 FileInfoLess());
686
687 _removeFiles.swap(newRemoveFiles);
688
689 return true;
690 }
691
692 bool
updateFiles(const LargeFileInfoSeq & files)693 PatcherI::updateFiles(const LargeFileInfoSeq& files)
694 {
695 DecompressorPtr decompressor = new Decompressor(_dataDir);
696 #if defined(__hppa)
697 //
698 // The thread stack size is only 64KB only HP-UX and that's not
699 // enough for this thread.
700 //
701 decompressor->start(256 * 1024); // 256KB
702 #else
703 decompressor->start();
704 #endif
705 bool result;
706
707 try
708 {
709 result = updateFilesInternal(files, decompressor);
710 }
711 catch(...)
712 {
713 decompressor->destroy();
714 decompressor->getThreadControl().join();
715 decompressor->log(_log);
716 throw;
717 }
718
719 decompressor->destroy();
720 decompressor->getThreadControl().join();
721 decompressor->log(_log);
722 decompressor->exception();
723
724 return result;
725 }
726
727 bool
updateFilesInternal(const LargeFileInfoSeq & files,const DecompressorPtr & decompressor)728 PatcherI::updateFilesInternal(const LargeFileInfoSeq& files, const DecompressorPtr& decompressor)
729 {
730 Long total = 0;
731 Long updated = 0;
732
733 for(LargeFileInfoSeq::const_iterator p = files.begin(); p != files.end(); ++p)
734 {
735 if(p->size > 0) // Regular, non-empty file?
736 {
737 total += p->size;
738 }
739 }
740
741 AsyncResultPtr curCB;
742 AsyncResultPtr nxtCB;
743
744 for(LargeFileInfoSeq::const_iterator p = files.begin(); p != files.end(); ++p)
745 {
746 if(p->size < 0) // Directory?
747 {
748 createDirectoryRecursive(_dataDir + '/' + p->path);
749 if(fputc('+', _log) == EOF || !writeFileInfo(_log, *p))
750 {
751 throw runtime_error("error writing log file:\n" + IceUtilInternal::lastErrorToString());
752 }
753 }
754 else // Regular file.
755 {
756 if(!_feedback->patchStart(p->path, p->size, updated, total))
757 {
758 return false;
759 }
760
761 if(p->size == 0)
762 {
763 string path = simplify(_dataDir + '/' + p->path);
764 FILE* fp = IceUtilInternal::fopen(path, "wb");
765 if(fp == 0)
766 {
767 throw runtime_error("cannot open `" + path +"' for writing:\n" + IceUtilInternal::lastErrorToString());
768 }
769 fclose(fp);
770 }
771 else
772 {
773 string pathBZ2 = simplify(_dataDir + '/' + p->path + ".bz2");
774
775 string dir = getDirname(pathBZ2);
776 if(!dir.empty())
777 {
778 createDirectoryRecursive(dir);
779 }
780
781 try
782 {
783 removeRecursive(pathBZ2);
784 }
785 catch(...)
786 {
787 }
788
789 FILE* fileBZ2 = IceUtilInternal::fopen(pathBZ2, "wb");
790 if(fileBZ2 == 0)
791 {
792 throw runtime_error("cannot open `" + pathBZ2 + "' for writing:\n" + IceUtilInternal::lastErrorToString());
793 }
794
795 try
796 {
797 Ice::Long pos = 0;
798
799 while(pos < p->size)
800 {
801 if(!curCB)
802 {
803 assert(!nxtCB);
804 curCB = _useSmallFileAPI ?
805 _serverNoCompress->begin_getFileCompressed(p->path, static_cast<Ice::Int>(pos), _chunkSize) :
806 _serverNoCompress->begin_getLargeFileCompressed(p->path, pos, _chunkSize);
807 }
808 else
809 {
810 assert(nxtCB);
811 swap(nxtCB, curCB);
812 }
813
814 if(pos + _chunkSize < p->size)
815 {
816 nxtCB = _useSmallFileAPI ?
817 _serverNoCompress->begin_getFileCompressed(p->path, static_cast<Ice::Int>(pos + _chunkSize), _chunkSize) :
818 _serverNoCompress->begin_getLargeFileCompressed(p->path, pos + _chunkSize, _chunkSize);
819 }
820 else
821 {
822 LargeFileInfoSeq::const_iterator q = p + 1;
823
824 while(q != files.end() && q->size <= 0)
825 {
826 ++q;
827 }
828
829 if(q != files.end())
830 {
831 nxtCB = _useSmallFileAPI ?
832 _serverNoCompress->begin_getFileCompressed(q->path, 0, _chunkSize) :
833 _serverNoCompress->begin_getLargeFileCompressed(q->path, 0, _chunkSize);
834 }
835 }
836
837 ByteSeq bytes;
838
839 try
840 {
841 bytes = _useSmallFileAPI ? _serverNoCompress->end_getFileCompressed(curCB) :
842 _serverNoCompress->end_getLargeFileCompressed(curCB);
843 }
844 catch(const FileAccessException& ex)
845 {
846 throw runtime_error("error from IcePatch2 server for `" + p->path + "': " + ex.reason);
847 }
848
849 if(bytes.empty())
850 {
851 throw runtime_error("size mismatch for `" + p->path + "'");
852 }
853
854 if(fwrite(reinterpret_cast<char*>(&bytes[0]), bytes.size(), 1, fileBZ2) != 1)
855 {
856 throw runtime_error(": cannot write `" + pathBZ2 + "':\n" + IceUtilInternal::lastErrorToString());
857 }
858
859 // 'bytes' is always returned with size '_chunkSize'. When a file is smaller than '_chunkSize'
860 // or we are reading the last chunk of a file, 'bytes' will be larger than necessary. In this
861 // case we calculate the current position and updated size based on the known file size.
862 size_t size = (pos + bytes.size()) > static_cast<size_t>(p->size) ?
863 static_cast<size_t>(p->size - pos) : bytes.size();
864
865 pos += size;
866 updated += size;
867
868 if(!_feedback->patchProgress(pos, p->size, updated, total))
869 {
870 fclose(fileBZ2);
871 return false;
872 }
873 }
874 }
875 catch(...)
876 {
877 fclose(fileBZ2);
878 throw;
879 }
880
881 fclose(fileBZ2);
882
883 decompressor->log(_log);
884 decompressor->add(*p);
885 }
886
887 if(!_feedback->patchEnd())
888 {
889 return false;
890 }
891 }
892 }
893
894 LargeFileInfoSeq newLocalFiles;
895 newLocalFiles.reserve(_localFiles.size());
896
897 set_union(_localFiles.begin(),
898 _localFiles.end(),
899 files.begin(),
900 files.end(),
901 back_inserter(newLocalFiles),
902 FileInfoLess());
903
904 _localFiles.swap(newLocalFiles);
905
906 LargeFileInfoSeq newUpdateFiles;
907
908 set_difference(_updateFiles.begin(),
909 _updateFiles.end(),
910 files.begin(),
911 files.end(),
912 back_inserter(newUpdateFiles),
913 FileInfoLess());
914
915 _updateFiles.swap(newUpdateFiles);
916
917 return true;
918 }
919
920 bool
updateFlags(const LargeFileInfoSeq & files)921 PatcherI::updateFlags(const LargeFileInfoSeq& files)
922 {
923 for(LargeFileInfoSeq::const_iterator p = files.begin(); p != files.end(); ++p)
924 {
925 if(p->size >= 0) // Regular file?
926 {
927 setFileFlags(_dataDir + '/' + p->path, *p);
928 }
929 }
930
931 //
932 // Remove the old files whose flags were updated from the set of
933 // local files.
934 //
935 LargeFileInfoSeq localFiles;
936 localFiles.reserve(_localFiles.size());
937 set_difference(_localFiles.begin(),
938 _localFiles.end(),
939 files.begin(),
940 files.end(),
941 back_inserter(localFiles),
942 FileInfoWithoutFlagsLess()); // NOTE: We ignore the flags.
943
944 //
945 // Add the new files to the set of local file.
946 //
947 _localFiles.clear();
948 set_union(localFiles.begin(),
949 localFiles.end(),
950 files.begin(),
951 files.end(),
952 back_inserter(_localFiles),
953 FileInfoLess());
954
955 LargeFileInfoSeq newUpdateFlags;
956
957 set_difference(_updateFlags.begin(),
958 _updateFlags.end(),
959 files.begin(),
960 files.end(),
961 back_inserter(newUpdateFlags),
962 FileInfoLess());
963
964 _updateFlags.swap(newUpdateFlags);
965
966 return true;
967 }
968
969 }
970
971 PatcherPtr
create(const Ice::CommunicatorPtr & communicator,const PatcherFeedbackPtr & feedback)972 PatcherFactory::create(const Ice::CommunicatorPtr& communicator, const PatcherFeedbackPtr& feedback)
973 {
974 return new PatcherI(communicator, feedback);
975 }
976
977 //
978 // Create a patcher with the given parameters. These parameters
979 // are equivalent to the configuration properties described above.
980 //
981 PatcherPtr
create(const FileServerPrx & server,const PatcherFeedbackPtr & feedback,const string & dataDir,bool thorough,Ice::Int chunkSize,Ice::Int remove)982 PatcherFactory::create(const FileServerPrx& server,
983 const PatcherFeedbackPtr& feedback,
984 const string& dataDir,
985 bool thorough,
986 Ice::Int chunkSize,
987 Ice::Int remove)
988 {
989 return new PatcherI(server, feedback, dataDir, thorough, chunkSize, remove);
990 }
991