volume.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #include "rar.hpp"
  2. #ifdef RARDLL
  3. static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize);
  4. static bool DllVolNotify(RAROptions *Cmd,wchar *NextName);
  5. #endif
  6. bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command)
  7. {
  8. RAROptions *Cmd=Arc.GetRAROptions();
  9. HEADER_TYPE HeaderType=Arc.GetHeaderType();
  10. FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead;
  11. bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) &&
  12. hd->SplitAfter;
  13. if (DataIO!=NULL && SplitHeader)
  14. {
  15. bool PackedHashPresent=Arc.Format==RARFMT50 ||
  16. hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff;
  17. if (PackedHashPresent &&
  18. !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL))
  19. uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName);
  20. }
  21. bool PrevVolEncrypted=Arc.Encrypted;
  22. int64 PosBeforeClose=Arc.Tell();
  23. if (DataIO!=NULL)
  24. DataIO->ProcessedArcSize+=DataIO->LastArcSize;
  25. Arc.Close();
  26. wchar NextName[NM];
  27. wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
  28. NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
  29. #if !defined(SFX_MODULE) && !defined(RARDLL)
  30. bool RecoveryDone=false;
  31. #endif
  32. bool OldSchemeTested=false;
  33. bool FailedOpen=false; // No more next volume open attempts if true.
  34. #if !defined(SILENT)
  35. // In -vp mode we force the pause before next volume even if it is present
  36. // and even if we are on the hard disk. It is important when user does not
  37. // want to process partially downloaded volumes preliminary.
  38. // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause
  39. // if we could open the next volume with FMF_OPENEXCLUSIVE. But another
  40. // developer asked us to return the previous behavior and always prompt
  41. // for confirmation. They want to control when unrar continues, because
  42. // the next file might not be fully decoded yet. They write chunks of data
  43. // and then close the file again until the next chunk comes in.
  44. if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName)))
  45. FailedOpen=true;
  46. #endif
  47. uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0;
  48. if (!FailedOpen)
  49. while (!Arc.Open(NextName,OpenMode))
  50. {
  51. // We need to open a new volume which size was not calculated
  52. // in total size before, so we cannot calculate the total progress
  53. // anymore. Let's reset the total size to zero and stop
  54. // the total progress.
  55. if (DataIO!=NULL)
  56. DataIO->TotalArcSize=0;
  57. if (!OldSchemeTested)
  58. {
  59. // Checking for new style volumes renamed by user to old style
  60. // name format. Some users did it for unknown reason.
  61. wchar AltNextName[NM];
  62. wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName));
  63. NextVolumeName(AltNextName,ASIZE(AltNextName),true);
  64. OldSchemeTested=true;
  65. if (Arc.Open(AltNextName,OpenMode))
  66. {
  67. wcsncpyz(NextName,AltNextName,ASIZE(NextName));
  68. break;
  69. }
  70. }
  71. #ifdef RARDLL
  72. if (!DllVolChange(Cmd,NextName,ASIZE(NextName)))
  73. {
  74. FailedOpen=true;
  75. break;
  76. }
  77. #else // !RARDLL
  78. #ifndef SFX_MODULE
  79. if (!RecoveryDone)
  80. {
  81. RecVolumesRestore(Cmd,Arc.FileName,true);
  82. RecoveryDone=true;
  83. continue;
  84. }
  85. #endif
  86. if (!Cmd->VolumePause && !IsRemovable(NextName))
  87. {
  88. FailedOpen=true;
  89. break;
  90. }
  91. #ifndef SILENT
  92. if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName)))
  93. #endif
  94. {
  95. FailedOpen=true;
  96. break;
  97. }
  98. #endif // RARDLL
  99. }
  100. if (FailedOpen)
  101. {
  102. uiMsg(UIERROR_MISSINGVOL,NextName);
  103. Arc.Open(Arc.FileName,OpenMode);
  104. Arc.Seek(PosBeforeClose,SEEK_SET);
  105. return false;
  106. }
  107. if (Command=='T' || Command=='X' || Command=='E')
  108. mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName);
  109. Arc.CheckArc(true);
  110. #ifdef RARDLL
  111. if (!DllVolNotify(Cmd,NextName))
  112. return false;
  113. #endif
  114. if (Arc.Encrypted!=PrevVolEncrypted)
  115. {
  116. // There is no legitimate reason for encrypted header state to be
  117. // changed in the middle of volume sequence. So we abort here to prevent
  118. // replacing an encrypted header volume to unencrypted and adding
  119. // unexpected files by third party to encrypted extraction.
  120. uiMsg(UIERROR_BADARCHIVE,Arc.FileName);
  121. ErrHandler.Exit(RARX_FATAL);
  122. }
  123. if (SplitHeader)
  124. Arc.SearchBlock(HeaderType);
  125. else
  126. Arc.ReadHeader();
  127. if (Arc.GetHeaderType()==HEAD_FILE)
  128. {
  129. Arc.ConvertAttributes();
  130. Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
  131. }
  132. if (ShowFileName && !Cmd->DisableNames)
  133. {
  134. mprintf(St(MExtrPoints),Arc.FileHead.FileName);
  135. if (!Cmd->DisablePercentage)
  136. mprintf(L" ");
  137. }
  138. if (DataIO!=NULL)
  139. {
  140. if (HeaderType==HEAD_ENDARC)
  141. DataIO->UnpVolume=false;
  142. else
  143. {
  144. DataIO->UnpVolume=hd->SplitAfter;
  145. DataIO->SetPackedSizeToRead(hd->PackSize);
  146. }
  147. DataIO->AdjustTotalArcSize(&Arc);
  148. // Reset the size of packed data read from current volume. It is used
  149. // to display the total progress and preceding volumes are already
  150. // compensated with ProcessedArcSize, so we need to reset this variable.
  151. DataIO->CurUnpRead=0;
  152. DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads);
  153. }
  154. return true;
  155. }
  156. #ifdef RARDLL
  157. bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
  158. {
  159. bool DllVolChanged=false,DllVolAborted=false;
  160. if (Cmd->Callback!=NULL)
  161. {
  162. wchar OrgNextName[NM];
  163. wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName));
  164. if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
  165. DllVolAborted=true;
  166. else
  167. if (wcscmp(OrgNextName,NextName)!=0)
  168. DllVolChanged=true;
  169. else
  170. {
  171. char NextNameA[NM],OrgNextNameA[NM];
  172. WideToChar(NextName,NextNameA,ASIZE(NextNameA));
  173. strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA));
  174. if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1)
  175. DllVolAborted=true;
  176. else
  177. if (strcmp(OrgNextNameA,NextNameA)!=0)
  178. {
  179. // We can damage some Unicode characters by U->A->U conversion,
  180. // so set Unicode name only if we see that ANSI name is changed.
  181. CharToWide(NextNameA,NextName,NameSize);
  182. DllVolChanged=true;
  183. }
  184. }
  185. }
  186. if (!DllVolChanged && Cmd->ChangeVolProc!=NULL)
  187. {
  188. char NextNameA[NM];
  189. WideToChar(NextName,NextNameA,ASIZE(NextNameA));
  190. int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK);
  191. if (RetCode==0)
  192. DllVolAborted=true;
  193. else
  194. CharToWide(NextNameA,NextName,NameSize);
  195. }
  196. // We quit only on 'abort' condition, but not on 'name not changed'.
  197. // It is legitimate for program to return the same name when waiting
  198. // for currently non-existent volume.
  199. // Also we quit to prevent an infinite loop if no callback is defined.
  200. if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL)
  201. {
  202. Cmd->DllError=ERAR_EOPEN;
  203. return false;
  204. }
  205. return true;
  206. }
  207. #endif
  208. #ifdef RARDLL
  209. bool DllVolNotify(RAROptions *Cmd,wchar *NextName)
  210. {
  211. char NextNameA[NM];
  212. WideToChar(NextName,NextNameA,ASIZE(NextNameA));
  213. if (Cmd->Callback!=NULL)
  214. {
  215. if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1)
  216. return false;
  217. if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1)
  218. return false;
  219. }
  220. if (Cmd->ChangeVolProc!=NULL)
  221. {
  222. int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY);
  223. if (RetCode==0)
  224. return false;
  225. }
  226. return true;
  227. }
  228. #endif