1
0

qopen.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include "rar.hpp"
  2. QuickOpen::QuickOpen()
  3. {
  4. Buf=NULL;
  5. Init(NULL,false);
  6. }
  7. QuickOpen::~QuickOpen()
  8. {
  9. Close();
  10. delete[] Buf;
  11. }
  12. void QuickOpen::Init(Archive *Arc,bool WriteMode)
  13. {
  14. if (Arc!=NULL) // Unless called from constructor.
  15. Close();
  16. QuickOpen::Arc=Arc;
  17. QuickOpen::WriteMode=WriteMode;
  18. ListStart=NULL;
  19. ListEnd=NULL;
  20. if (Buf==NULL)
  21. Buf=new byte[MaxBufSize];
  22. CurBufSize=0; // Current size of buffered data in write mode.
  23. Loaded=false;
  24. }
  25. void QuickOpen::Close()
  26. {
  27. QuickOpenItem *Item=ListStart;
  28. while (Item!=NULL)
  29. {
  30. QuickOpenItem *Next=Item->Next;
  31. delete[] Item->Header;
  32. delete Item;
  33. Item=Next;
  34. }
  35. }
  36. void QuickOpen::Load(uint64 BlockPos)
  37. {
  38. if (!Loaded)
  39. {
  40. // If loading for the first time, perform additional intialization.
  41. SeekPos=Arc->Tell();
  42. UnsyncSeekPos=false;
  43. int64 SavePos=SeekPos;
  44. Arc->Seek(BlockPos,SEEK_SET);
  45. // If BlockPos points to original main header, we'll have the infinite
  46. // recursion, because ReadHeader() for main header will attempt to load
  47. // QOpen and call QuickOpen::Load again. If BlockPos points to long chain
  48. // of other main headers, we'll have multiple recursive calls of this
  49. // function wasting resources. So we prohibit QOpen temporarily to
  50. // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
  51. // and QOpenOffset fields, so we cannot use them to prohibit QOpen.
  52. Arc->SetProhibitQOpen(true);
  53. size_t ReadSize=Arc->ReadHeader();
  54. Arc->SetProhibitQOpen(false);
  55. if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
  56. !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
  57. {
  58. Arc->Seek(SavePos,SEEK_SET);
  59. return;
  60. }
  61. QOHeaderPos=Arc->CurBlockPos;
  62. RawDataStart=Arc->Tell();
  63. RawDataSize=Arc->SubHead.UnpSize;
  64. Arc->Seek(SavePos,SEEK_SET);
  65. Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
  66. }
  67. if (Arc->SubHead.Encrypted)
  68. {
  69. RAROptions *Cmd=Arc->GetRAROptions();
  70. #ifndef RAR_NOCRYPT
  71. if (Cmd->Password.IsSet())
  72. Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
  73. Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
  74. Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
  75. else
  76. #endif
  77. {
  78. Loaded=false;
  79. return;
  80. }
  81. }
  82. RawDataPos=0;
  83. ReadBufSize=0;
  84. ReadBufPos=0;
  85. LastReadHeader.Reset();
  86. LastReadHeaderPos=0;
  87. ReadBuffer();
  88. }
  89. bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
  90. {
  91. if (!Loaded)
  92. return false;
  93. // Find next suitable cached block.
  94. while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
  95. if (!ReadNext())
  96. break;
  97. if (!Loaded)
  98. {
  99. // If something wrong happened, let's set the correct file pointer
  100. // and stop further quick open processing.
  101. if (UnsyncSeekPos)
  102. Arc->File::Seek(SeekPos,SEEK_SET);
  103. return false;
  104. }
  105. if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
  106. {
  107. memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
  108. Result=Size;
  109. SeekPos+=Size;
  110. UnsyncSeekPos=true;
  111. }
  112. else
  113. {
  114. if (UnsyncSeekPos)
  115. {
  116. Arc->File::Seek(SeekPos,SEEK_SET);
  117. UnsyncSeekPos=false;
  118. }
  119. int ReadSize=Arc->File::Read(Data,Size);
  120. if (ReadSize<0)
  121. {
  122. Loaded=false;
  123. return false;
  124. }
  125. Result=ReadSize;
  126. SeekPos+=ReadSize;
  127. }
  128. return true;
  129. }
  130. bool QuickOpen::Seek(int64 Offset,int Method)
  131. {
  132. if (!Loaded)
  133. return false;
  134. // Normally we process an archive sequentially from beginning to end,
  135. // so we read quick open data sequentially. But some operations like
  136. // archive updating involve several passes. So if we detect that file
  137. // pointer is moved back, we reload quick open data from beginning.
  138. if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
  139. Load(QOHeaderPos);
  140. if (Method==SEEK_SET)
  141. SeekPos=Offset;
  142. if (Method==SEEK_CUR)
  143. SeekPos+=Offset;
  144. UnsyncSeekPos=true;
  145. if (Method==SEEK_END)
  146. {
  147. Arc->File::Seek(Offset,SEEK_END);
  148. SeekPos=Arc->File::Tell();
  149. UnsyncSeekPos=false;
  150. }
  151. return true;
  152. }
  153. bool QuickOpen::Tell(int64 *Pos)
  154. {
  155. if (!Loaded)
  156. return false;
  157. *Pos=SeekPos;
  158. return true;
  159. }
  160. uint QuickOpen::ReadBuffer()
  161. {
  162. int64 SavePos=Arc->Tell();
  163. Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
  164. size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
  165. if (Arc->SubHead.Encrypted)
  166. SizeToRead &= ~CRYPT_BLOCK_MASK;
  167. int ReadSize=0;
  168. if (SizeToRead!=0)
  169. {
  170. ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
  171. if (ReadSize<=0)
  172. ReadSize=0;
  173. else
  174. {
  175. #ifndef RAR_NOCRYPT
  176. if (Arc->SubHead.Encrypted)
  177. Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
  178. #endif
  179. RawDataPos+=ReadSize;
  180. ReadBufSize+=ReadSize;
  181. }
  182. }
  183. Arc->Seek(SavePos,SEEK_SET);
  184. return ReadSize;
  185. }
  186. // Fill RawRead object from buffer.
  187. bool QuickOpen::ReadRaw(RawRead &Raw)
  188. {
  189. if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
  190. {
  191. // Ensure that we have enough data to read CRC and header size.
  192. size_t DataLeft=ReadBufSize-ReadBufPos;
  193. memcpy(Buf,Buf+ReadBufPos,DataLeft);
  194. ReadBufPos=0;
  195. ReadBufSize=DataLeft;
  196. ReadBuffer();
  197. }
  198. const size_t FirstReadSize=7;
  199. if (ReadBufPos+FirstReadSize>ReadBufSize)
  200. return false;
  201. Raw.Read(Buf+ReadBufPos,FirstReadSize);
  202. ReadBufPos+=FirstReadSize;
  203. uint SavedCRC=Raw.Get4();
  204. uint SizeBytes=Raw.GetVSize(4);
  205. uint64 BlockSize=Raw.GetV();
  206. int SizeToRead=int(BlockSize);
  207. SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
  208. if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
  209. {
  210. Loaded=false; // Invalid data.
  211. return false;
  212. }
  213. // If rest of block data crosses Buf boundary, read it in loop.
  214. while (SizeToRead>0)
  215. {
  216. size_t DataLeft=ReadBufSize-ReadBufPos;
  217. size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
  218. Raw.Read(Buf+ReadBufPos,CurSizeToRead);
  219. ReadBufPos+=CurSizeToRead;
  220. SizeToRead-=int(CurSizeToRead);
  221. if (SizeToRead>0) // We read the entire buffer and still need more data.
  222. {
  223. ReadBufPos=0;
  224. ReadBufSize=0;
  225. if (ReadBuffer()==0)
  226. return false;
  227. }
  228. }
  229. return SavedCRC==Raw.GetCRC50();
  230. }
  231. // Read next cached header.
  232. bool QuickOpen::ReadNext()
  233. {
  234. RawRead Raw(NULL);
  235. if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
  236. return false;
  237. uint Flags=(uint)Raw.GetV();
  238. uint64 Offset=Raw.GetV();
  239. size_t HeaderSize=(size_t)Raw.GetV();
  240. if (HeaderSize>MAX_HEADER_SIZE_RAR5)
  241. return false;
  242. LastReadHeader.Alloc(HeaderSize);
  243. Raw.GetB(&LastReadHeader[0],HeaderSize);
  244. // Calculate the absolute position as offset from quick open service header.
  245. LastReadHeaderPos=QOHeaderPos-Offset;
  246. return true;
  247. }