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