-
Notifications
You must be signed in to change notification settings - Fork 43
/
rdwrfn.cpp
326 lines (274 loc) · 8.29 KB
/
rdwrfn.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#include "rar.hpp"
ComprDataIO::ComprDataIO()
{
#ifndef RAR_NOCRYPT
Crypt=new CryptData;
Decrypt=new CryptData;
#endif
Init();
}
void ComprDataIO::Init()
{
UnpackFromMemory=false;
UnpackToMemory=false;
UnpPackedSize=0;
UnpPackedLeft=0;
ShowProgress=true;
TestMode=false;
SkipUnpCRC=false;
NoFileHeader=false;
PackVolume=false;
UnpVolume=false;
NextVolumeMissing=false;
SrcFile=NULL;
DestFile=NULL;
UnpWrAddr=NULL;
UnpWrSize=0;
Command=NULL;
Encryption=false;
Decryption=false;
CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0;
LastPercent=-1;
SubHead=NULL;
SubHeadPos=NULL;
CurrentCommand=0;
ProcessedArcSize=0;
LastArcSize=0;
TotalArcSize=0;
}
ComprDataIO::~ComprDataIO()
{
#ifndef RAR_NOCRYPT
delete Crypt;
delete Decrypt;
#endif
}
int ComprDataIO::UnpRead(byte *Addr,size_t Count)
{
#ifndef RAR_NOCRYPT
// In case of encryption we need to align read size to encryption
// block size. We can do it by simple masking, because unpack read code
// always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0.
if (Decryption)
Count &= ~CRYPT_BLOCK_MASK;
#endif
int ReadSize=0,TotalRead=0;
byte *ReadAddr;
ReadAddr=Addr;
while (Count > 0)
{
Archive *SrcArc=(Archive *)SrcFile;
if (UnpackFromMemory)
{
memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize);
ReadSize=(int)UnpackFromMemorySize;
UnpackFromMemorySize=0;
}
else
{
size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count;
if (SizeToRead > 0)
{
if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft)
{
// We need aligned blocks for decryption and we want "Keep broken
// files" to work efficiently with missing encrypted volumes.
// So for last data block in volume we adjust the size to read to
// next equal or smaller block producing aligned total block size.
// So we'll ask for next volume only when processing few unaligned
// bytes left in the end, when most of data is already extracted.
size_t NewTotalRead = TotalRead + SizeToRead;
size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK);
size_t NewSizeToRead = SizeToRead - Adjust;
if ((int)NewSizeToRead > 0)
SizeToRead = NewSizeToRead;
}
if (!SrcFile->IsOpened())
return -1;
ReadSize=SrcFile->Read(ReadAddr,SizeToRead);
FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead;
if (!NoFileHeader && hd->SplitAfter)
PackedDataHash.Update(ReadAddr,ReadSize);
}
}
CurUnpRead+=ReadSize;
TotalRead+=ReadSize;
#ifndef NOVOLUME
// These variable are not used in NOVOLUME mode, so it is better
// to exclude commands below to avoid compiler warnings.
ReadAddr+=ReadSize;
Count-=ReadSize;
#endif
UnpPackedLeft-=ReadSize;
// Do not ask for next volume if we read something from current volume.
// If next volume is missing, we need to process all data from current
// volume before aborting. It helps to recover all possible data
// in "Keep broken files" mode. But if we process encrypted data,
// we ask for next volume also if we have non-aligned encryption block.
// Since we adjust data size for decryption earlier above,
// it does not hurt "Keep broken files" mode efficiency.
if (UnpVolume && UnpPackedLeft == 0 &&
(ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) )
{
#ifndef NOVOLUME
if (!MergeArchive(*SrcArc,this,true,CurrentCommand))
#endif
{
NextVolumeMissing=true;
return -1;
}
}
else
break;
}
Archive *SrcArc=(Archive *)SrcFile;
if (SrcArc!=NULL)
ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize);
if (ReadSize!=-1)
{
ReadSize=TotalRead;
#ifndef RAR_NOCRYPT
if (Decryption)
Decrypt->DecryptBlock(Addr,ReadSize);
#endif
}
Wait();
return ReadSize;
}
void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#ifdef RARDLL
CommandData *Cmd=((Archive *)SrcFile)->GetCommandData();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1)
ErrHandler.Exit(RARX_USERBREAK);
if (Cmd->ProcessDataProc!=NULL)
{
int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
if (RetCode==0)
ErrHandler.Exit(RARX_USERBREAK);
}
}
#endif // RARDLL
UnpWrAddr=Addr;
UnpWrSize=Count;
if (UnpackToMemory)
{
if (Count <= UnpackToMemorySize)
{
memcpy(UnpackToMemoryAddr,Addr,Count);
UnpackToMemoryAddr+=Count;
UnpackToMemorySize-=Count;
}
}
else
if (!TestMode)
DestFile->Write(Addr,Count);
CurUnpWrite+=Count;
if (!SkipUnpCRC)
UnpHash.Update(Addr,Count);
ShowUnpWrite();
Wait();
}
void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
{
if (ShowProgress && SrcFile!=NULL)
{
// Important when processing several archives or multivolume archive.
ArcPos+=ProcessedArcSize;
Archive *SrcArc=(Archive *)SrcFile;
CommandData *Cmd=SrcArc->GetCommandData();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
{
uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize);
LastPercent=CurPercent;
}
}
}
void ComprDataIO::ShowUnpWrite()
{
}
void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
{
if (SrcFile!=NULL)
ComprDataIO::SrcFile=SrcFile;
if (DestFile!=NULL)
ComprDataIO::DestFile=DestFile;
LastPercent=-1;
}
void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
{
*Data=UnpWrAddr;
*Size=UnpWrSize;
}
// Return true if encryption or decryption mode is set correctly.
bool ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,const byte *InitV,
uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
#ifdef RAR_NOCRYPT
return false;
#else
if (Encrypt)
{
Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Encryption;
}
else
{
Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
return Decryption;
}
#endif
}
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetCmt13Encryption()
{
Decryption=true;
Decrypt->SetCmt13Encryption();
}
#endif
void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
{
UnpackToMemory=true;
UnpackToMemoryAddr=Addr;
UnpackToMemorySize=Size;
}
// Extraction progress is based on the position in archive and we adjust
// the total archives size here, so trailing blocks do not prevent progress
// reaching 100% at the end of extraction. Alternatively we could print "100%"
// after completing the entire archive extraction, but then we would need
// to take into account possible messages like the checksum error after
// last file percent progress.
void ComprDataIO::AdjustTotalArcSize(Archive *Arc)
{
// If we know a position of QO or RR blocks, use them to adjust the total
// packed size to beginning of these blocks. Earlier we already calculated
// the total size based on entire archive sizes. We also set LastArcSize
// to start of first trailing block, to add it later to ProcessedArcSize.
uint64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0;
// QO is always preceding RR record.
// Also we check QO and RR to be less than archive length to prevent
// negative "ArcLength-LastArcSize" and possible signed integer overflow
// when calculating TotalArcSize.
if (Arc->MainHead.QOpenOffset>0 && Arc->MainHead.QOpenOffset<ArcLength)
LastArcSize=Arc->MainHead.QOpenOffset;
else
if (Arc->MainHead.RROffset>0 && Arc->MainHead.RROffset<ArcLength)
LastArcSize=Arc->MainHead.RROffset;
else
{
// If neither QO nor RR are found, exclude the approximate size of
// end of archive block.
// We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC,
// but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file
// name, so we do not have two files with 100% at the end of archive.
const uint EndBlock=23;
if (ArcLength>EndBlock)
LastArcSize=ArcLength-EndBlock;
}
TotalArcSize-=ArcLength-LastArcSize;
}