Line | Count | Source (jump to first uncovered line) |
1 | | #include "rar.hpp" |
2 | | |
3 | | size_t Archive::ReadHeader() |
4 | 161k | { |
5 | | // Once we failed to decrypt an encrypted block, there is no reason to |
6 | | // attempt to do it further. We'll never be successful and only generate |
7 | | // endless errors. |
8 | 161k | if (FailedHeaderDecryption) |
9 | 1 | return 0; |
10 | | |
11 | 161k | CurBlockPos=Tell(); |
12 | | |
13 | | // Other developers asked us to initialize it to suppress "may be used |
14 | | // uninitialized" warning in code below in some compilers. |
15 | 161k | size_t ReadSize=0; |
16 | | |
17 | 161k | switch(Format) |
18 | 161k | { |
19 | 0 | #ifndef SFX_MODULE |
20 | 6.13k | case RARFMT14: |
21 | 6.13k | ReadSize=ReadHeader14(); |
22 | 6.13k | break; |
23 | 24.6k | case RARFMT15: |
24 | 24.6k | ReadSize=ReadHeader15(); |
25 | 24.6k | break; |
26 | 0 | #endif |
27 | 130k | case RARFMT50: |
28 | 130k | ReadSize=ReadHeader50(); |
29 | 130k | break; |
30 | 161k | } |
31 | | |
32 | | // It is important to check ReadSize>0 here, because it is normal |
33 | | // for RAR2 and RAR3 archives without end of archive block to have |
34 | | // NextBlockPos==CurBlockPos after the end of archive has reached. |
35 | 161k | if (ReadSize>0 && NextBlockPos<=CurBlockPos) |
36 | 249 | { |
37 | 249 | BrokenHeaderMsg(); |
38 | 249 | ReadSize=0; |
39 | 249 | } |
40 | | |
41 | 161k | if (ReadSize==0) |
42 | 17.7k | CurHeaderType=HEAD_UNKNOWN; |
43 | | |
44 | 161k | return ReadSize; |
45 | 161k | } |
46 | | |
47 | | |
48 | | size_t Archive::SearchBlock(HEADER_TYPE HeaderType) |
49 | 0 | { |
50 | 0 | size_t Size,Count=0; |
51 | 0 | while ((Size=ReadHeader())!=0 && |
52 | 0 | (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) |
53 | 0 | { |
54 | 0 | if ((++Count & 127)==0) |
55 | 0 | Wait(); |
56 | 0 | if (GetHeaderType()==HeaderType) |
57 | 0 | return Size; |
58 | 0 | SeekToNext(); |
59 | 0 | } |
60 | 0 | return 0; |
61 | 0 | } |
62 | | |
63 | | |
64 | | size_t Archive::SearchSubBlock(const wchar *Type) |
65 | 105 | { |
66 | 105 | size_t Size,Count=0; |
67 | 3.67k | while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) |
68 | 3.56k | { |
69 | 3.56k | if ((++Count & 127)==0) |
70 | 18 | Wait(); |
71 | 3.56k | if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) |
72 | 0 | return Size; |
73 | 3.56k | SeekToNext(); |
74 | 3.56k | } |
75 | 105 | return 0; |
76 | 105 | } |
77 | | |
78 | | |
79 | | size_t Archive::SearchRR() |
80 | 0 | { |
81 | | // If locator extra field is available for recovery record, let's utilize it. |
82 | 0 | if (MainHead.Locator && MainHead.RROffset!=0) |
83 | 0 | { |
84 | 0 | uint64 CurPos=Tell(); |
85 | 0 | Seek(MainHead.RROffset,SEEK_SET); |
86 | 0 | size_t Size=ReadHeader(); |
87 | 0 | if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) |
88 | 0 | return Size; |
89 | 0 | Seek(CurPos,SEEK_SET); |
90 | 0 | } |
91 | | // Otherwise scan the entire archive to find the recovery record. |
92 | 0 | return SearchSubBlock(SUBHEAD_TYPE_RR); |
93 | 0 | } |
94 | | |
95 | | |
96 | | void Archive::UnexpEndArcMsg() |
97 | 7.94k | { |
98 | 7.94k | int64 ArcSize=FileLength(); |
99 | | |
100 | | // If block positions are equal to file size, this is not an error. |
101 | | // It can happen when we reached the end of older RAR 1.5 archive, |
102 | | // which did not have the end of archive block. |
103 | | // We can't replace this check by checking that read size is exactly 0 |
104 | | // in the beginning of file header, because in this case the read position |
105 | | // still can be beyond the end of archive. |
106 | 7.94k | if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) |
107 | 7.79k | { |
108 | 7.79k | uiMsg(UIERROR_UNEXPEOF,FileName); |
109 | 7.79k | if (CurHeaderType!=HEAD_FILE) |
110 | 2.77k | uiMsg(UIERROR_TRUNCSERVICE,FileName,SubHead.FileName); |
111 | | |
112 | 7.79k | ErrHandler.SetErrorCode(RARX_WARNING); |
113 | 7.79k | } |
114 | 7.94k | } |
115 | | |
116 | | |
117 | | void Archive::BrokenHeaderMsg() |
118 | 130k | { |
119 | 130k | uiMsg(UIERROR_HEADERBROKEN,FileName); |
120 | 130k | BrokenHeader=true; |
121 | 130k | ErrHandler.SetErrorCode(RARX_CRC); |
122 | 130k | } |
123 | | |
124 | | |
125 | | void Archive::UnkEncVerMsg(const std::wstring &Name,const std::wstring &Info) |
126 | 2.24k | { |
127 | 2.24k | uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); |
128 | 2.24k | ErrHandler.SetErrorCode(RARX_FATAL); |
129 | 2.24k | } |
130 | | |
131 | | |
132 | | // Return f in case of signed integer overflow or negative parameters |
133 | | // or v1+v2 otherwise. We use it for file offsets, which are signed |
134 | | // for compatibility with off_t in POSIX file functions and third party code. |
135 | | // Signed integer overflow is the undefined behavior according to |
136 | | // C++ standard and it causes fuzzers to complain. |
137 | | inline int64 SafeAdd(int64 v1,int64 v2,int64 f) |
138 | 130k | { |
139 | 130k | return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f; |
140 | 130k | } |
141 | | |
142 | | |
143 | | #ifndef SFX_MODULE |
144 | | size_t Archive::ReadHeader15() |
145 | 24.6k | { |
146 | 24.6k | RawRead Raw(this); |
147 | | |
148 | 24.6k | bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; |
149 | | |
150 | 24.6k | if (Decrypt) |
151 | 27 | { |
152 | | #ifdef RAR_NOCRYPT // For rarext.dll, Setup.SFX and unrar_nocrypt.dll. |
153 | | return 0; |
154 | | #else |
155 | 27 | RequestArcPassword(NULL); |
156 | | |
157 | 27 | byte Salt[SIZE_SALT30]; |
158 | 27 | if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) |
159 | 0 | { |
160 | 0 | UnexpEndArcMsg(); |
161 | 0 | return 0; |
162 | 0 | } |
163 | 27 | HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); |
164 | 27 | Raw.SetCrypt(&HeadersCrypt); |
165 | 27 | #endif |
166 | 27 | } |
167 | | |
168 | 24.6k | Raw.Read(SIZEOF_SHORTBLOCKHEAD); |
169 | 24.6k | if (Raw.Size()==0) |
170 | 3.33k | { |
171 | 3.33k | UnexpEndArcMsg(); |
172 | 3.33k | return 0; |
173 | 3.33k | } |
174 | | |
175 | 21.3k | ShortBlock.HeadCRC=Raw.Get2(); |
176 | | |
177 | 21.3k | ShortBlock.Reset(); |
178 | | |
179 | 21.3k | uint HeaderType=Raw.Get1(); |
180 | 21.3k | ShortBlock.Flags=Raw.Get2(); |
181 | 21.3k | ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0; |
182 | 21.3k | ShortBlock.HeadSize=Raw.Get2(); |
183 | | |
184 | 21.3k | ShortBlock.HeaderType=(HEADER_TYPE)HeaderType; |
185 | 21.3k | if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD) |
186 | 4.35k | { |
187 | 4.35k | BrokenHeaderMsg(); |
188 | 4.35k | return 0; |
189 | 4.35k | } |
190 | | |
191 | | // For simpler further processing we map header types common |
192 | | // for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include |
193 | | // header types specific for RAR 1.5 - 4.x only. |
194 | 16.9k | switch(ShortBlock.HeaderType) |
195 | 16.9k | { |
196 | 82 | case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break; |
197 | 22 | case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break; |
198 | 157 | case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break; |
199 | 6 | case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break; |
200 | 16.9k | } |
201 | 16.9k | CurHeaderType=ShortBlock.HeaderType; |
202 | | |
203 | 16.9k | if (ShortBlock.HeaderType==HEAD3_CMT) |
204 | 41 | { |
205 | | // Old style (up to RAR 2.9) comment header embedded into main |
206 | | // or file header. We must not read the entire ShortBlock.HeadSize here |
207 | | // to not break the comment processing logic later. |
208 | 41 | Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD); |
209 | 41 | } |
210 | 16.8k | else |
211 | 16.8k | if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0) |
212 | 1.33k | { |
213 | | // Old style (up to RAR 2.9) main archive comment embedded into |
214 | | // the main archive header found. While we can read the entire |
215 | | // ShortBlock.HeadSize here and remove this part of "if", it would be |
216 | | // waste of memory, because we'll read and process this comment data |
217 | | // in other function anyway and we do not need them here now. |
218 | 1.33k | Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD); |
219 | 1.33k | } |
220 | 15.5k | else |
221 | 15.5k | Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD); |
222 | | |
223 | 16.9k | NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); |
224 | | |
225 | 16.9k | switch(ShortBlock.HeaderType) |
226 | 16.9k | { |
227 | 8.38k | case HEAD_MAIN: |
228 | 8.38k | MainHead.Reset(); |
229 | 8.38k | MainHead.SetBaseBlock(ShortBlock); |
230 | 8.38k | MainHead.HighPosAV=Raw.Get2(); |
231 | 8.38k | MainHead.PosAV=Raw.Get4(); |
232 | | |
233 | 8.38k | Volume=(MainHead.Flags & MHD_VOLUME)!=0; |
234 | 8.38k | Solid=(MainHead.Flags & MHD_SOLID)!=0; |
235 | 8.38k | Locked=(MainHead.Flags & MHD_LOCK)!=0; |
236 | 8.38k | Protected=(MainHead.Flags & MHD_PROTECT)!=0; |
237 | 8.38k | Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0; |
238 | 8.38k | Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0; |
239 | 8.38k | MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0; |
240 | | |
241 | | // Only for encrypted 3.0+ archives. 2.x archives did not have this |
242 | | // flag, so for non-encrypted archives, we'll set it later based on |
243 | | // file attributes. |
244 | 8.38k | FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0; |
245 | | |
246 | 8.38k | NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0; |
247 | 8.38k | break; |
248 | 5.04k | case HEAD_FILE: |
249 | 6.36k | case HEAD_SERVICE: |
250 | 6.36k | { |
251 | 6.36k | bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; |
252 | 6.36k | FileHeader *hd=FileBlock ? &FileHead:&SubHead; |
253 | 6.36k | hd->Reset(); |
254 | | |
255 | 6.36k | hd->SetBaseBlock(ShortBlock); |
256 | | |
257 | 6.36k | hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; |
258 | 6.36k | hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; |
259 | 6.36k | hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; |
260 | 6.36k | hd->SaltSet=(hd->Flags & LHD_SALT)!=0; |
261 | | |
262 | | // RAR versions earlier than 2.0 do not set the solid flag |
263 | | // in file header. They use only a global solid archive flag. |
264 | 6.36k | hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; |
265 | | |
266 | 6.36k | hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; |
267 | 6.36k | hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; |
268 | 6.36k | hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); |
269 | 6.36k | hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; |
270 | 6.36k | hd->Version=(hd->Flags & LHD_VERSION)!=0; |
271 | | |
272 | 6.36k | hd->DataSize=Raw.Get4(); |
273 | 6.36k | uint LowUnpSize=Raw.Get4(); |
274 | 6.36k | hd->HostOS=Raw.Get1(); |
275 | | |
276 | 6.36k | hd->FileHash.Type=HASH_CRC32; |
277 | 6.36k | hd->FileHash.CRC32=Raw.Get4(); |
278 | | |
279 | 6.36k | uint FileTime=Raw.Get4(); |
280 | 6.36k | hd->UnpVer=Raw.Get1(); |
281 | | |
282 | 6.36k | hd->Method=Raw.Get1()-0x30; |
283 | 6.36k | size_t NameSize=Raw.Get2(); |
284 | 6.36k | hd->FileAttr=Raw.Get4(); |
285 | | |
286 | | // RAR15 did not use the special dictionary size to mark dirs. |
287 | 6.36k | if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) |
288 | 211 | hd->Dir=true; |
289 | | |
290 | 6.36k | hd->CryptMethod=CRYPT_NONE; |
291 | 6.36k | if (hd->Encrypted) |
292 | 1.30k | switch(hd->UnpVer) |
293 | 1.30k | { |
294 | 15 | case 13: hd->CryptMethod=CRYPT_RAR13; break; |
295 | 16 | case 15: hd->CryptMethod=CRYPT_RAR15; break; |
296 | 12 | case 20: |
297 | 32 | case 26: hd->CryptMethod=CRYPT_RAR20; break; |
298 | 1.24k | default: hd->CryptMethod=CRYPT_RAR30; break; |
299 | 1.30k | } |
300 | | |
301 | 6.36k | hd->HSType=HSYS_UNKNOWN; |
302 | 6.36k | if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) |
303 | 719 | hd->HSType=HSYS_UNIX; |
304 | 5.64k | else |
305 | 5.64k | if (hd->HostOS<HOST_MAX) |
306 | 1.47k | hd->HSType=HSYS_WINDOWS; |
307 | | |
308 | 6.36k | hd->RedirType=FSREDIR_NONE; |
309 | | |
310 | | // RAR 4.x Unix symlink. |
311 | 6.36k | if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) |
312 | 374 | { |
313 | 374 | hd->RedirType=FSREDIR_UNIXSYMLINK; |
314 | 374 | hd->RedirName.clear(); |
315 | 374 | } |
316 | | |
317 | 6.36k | hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; |
318 | | |
319 | 6.36k | hd->LargeFile=(hd->Flags & LHD_LARGE)!=0; |
320 | | |
321 | 6.36k | uint HighPackSize,HighUnpSize; |
322 | 6.36k | if (hd->LargeFile) |
323 | 3.11k | { |
324 | 3.11k | HighPackSize=Raw.Get4(); |
325 | 3.11k | HighUnpSize=Raw.Get4(); |
326 | 3.11k | hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff); |
327 | 3.11k | } |
328 | 3.25k | else |
329 | 3.25k | { |
330 | 3.25k | HighPackSize=HighUnpSize=0; |
331 | | // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates |
332 | | // that we do not know the unpacked file size and must unpack it |
333 | | // until we find the end of file marker in compressed data. |
334 | 3.25k | hd->UnknownUnpSize=(LowUnpSize==0xffffffff); |
335 | 3.25k | } |
336 | 6.36k | hd->PackSize=INT32TO64(HighPackSize,hd->DataSize); |
337 | 6.36k | hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize); |
338 | 6.36k | if (hd->UnknownUnpSize) |
339 | 71 | hd->UnpSize=INT64NDF; |
340 | | |
341 | 6.36k | size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); |
342 | 6.36k | std::string FileName(ReadNameSize,0); |
343 | 6.36k | Raw.GetB((byte *)&FileName[0],ReadNameSize); |
344 | | |
345 | 6.36k | if (FileBlock) |
346 | 5.04k | { |
347 | 5.04k | hd->FileName.clear(); |
348 | 5.04k | if ((hd->Flags & LHD_UNICODE)!=0) |
349 | 2.05k | { |
350 | 2.05k | EncodeFileName NameCoder; |
351 | 2.05k | size_t Length=strlen(FileName.data()); |
352 | 2.05k | Length++; |
353 | 2.05k | if (ReadNameSize>Length) |
354 | 1.76k | NameCoder.Decode(FileName.data(),ReadNameSize, |
355 | 1.76k | (byte *)&FileName[Length], |
356 | 1.76k | ReadNameSize-Length,hd->FileName); |
357 | 2.05k | } |
358 | | |
359 | 5.04k | if (hd->FileName.empty()) |
360 | 3.29k | ArcCharToWide(FileName.data(),hd->FileName,ACTW_OEM); |
361 | | |
362 | 5.04k | #ifndef SFX_MODULE |
363 | 5.04k | ConvertNameCase(hd->FileName); |
364 | 5.04k | #endif |
365 | 5.04k | ConvertFileHeader(hd); |
366 | 5.04k | } |
367 | 1.31k | else |
368 | 1.31k | { |
369 | 1.31k | CharToWide(FileName.data(),hd->FileName); |
370 | | |
371 | | // Calculate the size of optional data. |
372 | 1.31k | int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); |
373 | 1.31k | if ((hd->Flags & LHD_SALT)!=0) |
374 | 333 | DataSize-=SIZE_SALT30; |
375 | | |
376 | 1.31k | if (DataSize>0) |
377 | 302 | { |
378 | | // Here we read optional additional fields for subheaders. |
379 | | // They are stored after the file name and before salt. |
380 | 302 | hd->SubData.resize(DataSize); |
381 | 302 | Raw.GetB(hd->SubData.data(),DataSize); |
382 | | |
383 | 302 | } |
384 | | |
385 | 1.31k | if (hd->CmpName(SUBHEAD_TYPE_CMT)) |
386 | 0 | MainComment=true; |
387 | 1.31k | } |
388 | | |
389 | 6.36k | if ((hd->Flags & LHD_SALT)!=0) |
390 | 3.10k | Raw.GetB(hd->Salt,SIZE_SALT30); |
391 | 6.36k | hd->mtime.SetDos(FileTime); |
392 | 6.36k | if ((hd->Flags & LHD_EXTTIME)!=0) |
393 | 1.97k | { |
394 | 1.97k | ushort Flags=Raw.Get2(); |
395 | 1.97k | RarTime *tbl[4]; |
396 | 1.97k | tbl[0]=&FileHead.mtime; |
397 | 1.97k | tbl[1]=&FileHead.ctime; |
398 | 1.97k | tbl[2]=&FileHead.atime; |
399 | 1.97k | tbl[3]=NULL; // Archive time is not used now. |
400 | 9.87k | for (int I=0;I<4;I++) |
401 | 7.90k | { |
402 | 7.90k | RarTime *CurTime=tbl[I]; |
403 | 7.90k | uint rmode=Flags>>(3-I)*4; |
404 | 7.90k | if ((rmode & 8)==0 || CurTime==NULL) |
405 | 7.76k | continue; |
406 | 134 | if (I!=0) |
407 | 90 | { |
408 | 90 | uint DosTime=Raw.Get4(); |
409 | 90 | CurTime->SetDos(DosTime); |
410 | 90 | } |
411 | 134 | RarLocalTime rlt; |
412 | 134 | CurTime->GetLocal(&rlt); |
413 | 134 | if (rmode & 4) |
414 | 83 | rlt.Second++; |
415 | 134 | rlt.Reminder=0; |
416 | 134 | uint count=rmode&3; |
417 | 375 | for (uint J=0;J<count;J++) |
418 | 241 | { |
419 | 241 | byte CurByte=Raw.Get1(); |
420 | 241 | rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8)); |
421 | 241 | } |
422 | | // Convert from 100ns RAR precision to REMINDER_PRECISION. |
423 | 134 | rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000; |
424 | 134 | CurTime->SetLocal(&rlt); |
425 | 134 | } |
426 | 1.97k | } |
427 | | // Set to 0 in case of overflow, so end of ReadHeader cares about it. |
428 | 6.36k | NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); |
429 | | |
430 | 6.36k | bool CRCProcessedOnly=hd->CommentInHeader; |
431 | 6.36k | uint HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); |
432 | 6.36k | if (hd->HeadCRC!=HeaderCRC) |
433 | 6.35k | { |
434 | 6.35k | BrokenHeader=true; |
435 | 6.35k | ErrHandler.SetErrorCode(RARX_WARNING); |
436 | | |
437 | | // If we have a broken encrypted header, we do not need to display |
438 | | // the error message here, because it will be displayed for such |
439 | | // headers later in this function. Also such headers are unlikely |
440 | | // to have anything sensible in file name field, so it is useless |
441 | | // to display the file name. |
442 | 6.35k | if (!Decrypt) |
443 | 6.35k | uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); |
444 | 6.35k | } |
445 | 6.36k | } |
446 | 0 | break; |
447 | 270 | case HEAD_ENDARC: |
448 | 270 | EndArcHead.SetBaseBlock(ShortBlock); |
449 | 270 | EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; |
450 | 270 | EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; |
451 | 270 | EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; |
452 | 270 | EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0; |
453 | 270 | if (EndArcHead.DataCRC) |
454 | 22 | EndArcHead.ArcDataCRC=Raw.Get4(); |
455 | 270 | if (EndArcHead.StoreVolNumber) |
456 | 16 | VolNumber=EndArcHead.VolNumber=Raw.Get2(); |
457 | 270 | break; |
458 | 0 | #ifndef SFX_MODULE |
459 | 41 | case HEAD3_CMT: |
460 | 41 | CommHead.SetBaseBlock(ShortBlock); |
461 | 41 | CommHead.UnpSize=Raw.Get2(); |
462 | 41 | CommHead.UnpVer=Raw.Get1(); |
463 | 41 | CommHead.Method=Raw.Get1(); |
464 | 41 | CommHead.CommCRC=Raw.Get2(); |
465 | 41 | break; |
466 | 54 | case HEAD3_PROTECT: |
467 | 54 | ProtectHead.SetBaseBlock(ShortBlock); |
468 | 54 | ProtectHead.DataSize=Raw.Get4(); |
469 | 54 | ProtectHead.Version=Raw.Get1(); |
470 | 54 | ProtectHead.RecSectors=Raw.Get2(); |
471 | 54 | ProtectHead.TotalBlocks=Raw.Get4(); |
472 | 54 | Raw.GetB(ProtectHead.Mark,8); |
473 | 54 | NextBlockPos+=ProtectHead.DataSize; |
474 | 54 | break; |
475 | 136 | case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. |
476 | 136 | SubBlockHead.SetBaseBlock(ShortBlock); |
477 | 136 | SubBlockHead.DataSize=Raw.Get4(); |
478 | 136 | NextBlockPos+=SubBlockHead.DataSize; |
479 | 136 | SubBlockHead.SubType=Raw.Get2(); |
480 | 136 | SubBlockHead.Level=Raw.Get1(); |
481 | 136 | switch(SubBlockHead.SubType) |
482 | 136 | { |
483 | 6 | case NTACL_HEAD: |
484 | 6 | *(SubBlockHeader *)&EAHead=SubBlockHead; |
485 | 6 | EAHead.UnpSize=Raw.Get4(); |
486 | 6 | EAHead.UnpVer=Raw.Get1(); |
487 | 6 | EAHead.Method=Raw.Get1(); |
488 | 6 | EAHead.EACRC=Raw.Get4(); |
489 | 6 | break; |
490 | 27 | case STREAM_HEAD: |
491 | 27 | *(SubBlockHeader *)&StreamHead=SubBlockHead; |
492 | 27 | StreamHead.UnpSize=Raw.Get4(); |
493 | 27 | StreamHead.UnpVer=Raw.Get1(); |
494 | 27 | StreamHead.Method=Raw.Get1(); |
495 | 27 | StreamHead.StreamCRC=Raw.Get4(); |
496 | 27 | StreamHead.StreamNameSize=Raw.Get2(); |
497 | | |
498 | 27 | const size_t MaxStreamName20=260; // Maximum allowed stream name in RAR 2.x format. |
499 | 27 | if (StreamHead.StreamNameSize>MaxStreamName20) |
500 | 15 | StreamHead.StreamNameSize=MaxStreamName20; |
501 | | |
502 | 27 | StreamHead.StreamName.resize(StreamHead.StreamNameSize); |
503 | 27 | Raw.GetB(&StreamHead.StreamName[0],StreamHead.StreamNameSize); |
504 | 27 | break; |
505 | 136 | } |
506 | 136 | break; |
507 | 136 | #endif |
508 | 1.68k | default: |
509 | 1.68k | if (ShortBlock.Flags & LONG_BLOCK) |
510 | 226 | NextBlockPos+=Raw.Get4(); |
511 | 1.68k | break; |
512 | 16.9k | } |
513 | | |
514 | 16.9k | uint HeaderCRC=Raw.GetCRC15(false); |
515 | | |
516 | | // Old AV header does not have header CRC properly set. |
517 | | // Old Unix owners header didn't include string fields into header size, |
518 | | // but included them into CRC, so it couldn't be verified with generic |
519 | | // approach here. |
520 | 16.9k | if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && |
521 | 16.9k | ShortBlock.HeaderType!=HEAD3_AV && |
522 | 16.9k | (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD)) |
523 | 16.8k | { |
524 | 16.8k | bool Recovered=false; |
525 | 16.8k | if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) |
526 | 246 | { |
527 | | // Last 7 bytes of recovered volume can contain zeroes, because |
528 | | // REV files store its own information (volume number, etc.) here. |
529 | 246 | int64 Length=Tell(); |
530 | 246 | Seek(Length-7,SEEK_SET); |
531 | 246 | Recovered=true; |
532 | 1.96k | for (int J=0;J<7;J++) |
533 | 1.72k | if (GetByte()!=0) |
534 | 1.65k | Recovered=false; |
535 | 246 | } |
536 | 16.8k | if (!Recovered) |
537 | 16.8k | { |
538 | 16.8k | BrokenHeader=true; |
539 | 16.8k | ErrHandler.SetErrorCode(RARX_CRC); |
540 | | |
541 | 16.8k | if (Decrypt) |
542 | 0 | { |
543 | 0 | uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); |
544 | 0 | FailedHeaderDecryption=true; |
545 | 0 | return 0; |
546 | 0 | } |
547 | 16.8k | } |
548 | 16.8k | } |
549 | | |
550 | 16.9k | return Raw.Size(); |
551 | 16.9k | } |
552 | | #endif // #ifndef SFX_MODULE |
553 | | |
554 | | |
555 | | size_t Archive::ReadHeader50() |
556 | 130k | { |
557 | 130k | RawRead Raw(this); |
558 | | |
559 | 130k | bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; |
560 | | |
561 | 130k | if (Decrypt) |
562 | 66 | { |
563 | | #if defined(RAR_NOCRYPT) |
564 | | return 0; |
565 | | #else |
566 | | |
567 | 66 | if (Cmd->SkipEncrypted) |
568 | 0 | { |
569 | 0 | uiMsg(UIMSG_SKIPENCARC,FileName); |
570 | 0 | FailedHeaderDecryption=true; // Suppress error messages and quit quietly. |
571 | 0 | return 0; |
572 | 0 | } |
573 | | |
574 | 66 | byte HeadersInitV[SIZE_INITV]; |
575 | 66 | if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) |
576 | 62 | { |
577 | 62 | UnexpEndArcMsg(); |
578 | 62 | return 0; |
579 | 62 | } |
580 | | |
581 | | // We repeat the password request only for manually entered passwords |
582 | | // and not for -p<pwd>. Wrong password can be intentionally provided |
583 | | // in -p<pwd> to not stop batch processing for encrypted archives. |
584 | 4 | bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); |
585 | | |
586 | 4 | RarCheckPassword CheckPwd; |
587 | 4 | if (CryptHead.UsePswCheck && !BrokenHeader) |
588 | 0 | CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck); |
589 | | |
590 | 4 | while (true) // Repeat the password prompt for wrong passwords. |
591 | 4 | { |
592 | 4 | RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); |
593 | | |
594 | 4 | byte PswCheck[SIZE_PSWCHECK]; |
595 | 4 | bool EncSet=HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); |
596 | | // Verify password validity. If header is damaged, we cannot rely on |
597 | | // password check value, because it can be damaged too. |
598 | 4 | if (EncSet && CryptHead.UsePswCheck && !BrokenHeader && |
599 | 4 | memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) |
600 | 0 | { |
601 | 0 | if (GlobalPassword) // For -p<pwd> or Ctrl+P. |
602 | 0 | { |
603 | | // This message is used by Android GUI to reset cached passwords. |
604 | | // Update appropriate code if changed. |
605 | 0 | uiMsg(UIERROR_BADPSW,FileName,FileName); |
606 | 0 | FailedHeaderDecryption=true; |
607 | 0 | ErrHandler.SetErrorCode(RARX_BADPWD); |
608 | 0 | return 0; |
609 | 0 | } |
610 | 0 | else // For passwords entered manually. |
611 | 0 | { |
612 | | // This message is used by Android GUI and Windows GUI and SFX to |
613 | | // reset cached passwords. Update appropriate code if changed. |
614 | 0 | uiMsg(UIWAIT_BADPSW,FileName,FileName); |
615 | 0 | Cmd->Password.Clean(); |
616 | 0 | } |
617 | | |
618 | 0 | #ifdef RARDLL |
619 | | // Avoid new requests for unrar.dll to prevent the infinite loop |
620 | | // if app always returns the same password. |
621 | 0 | ErrHandler.SetErrorCode(RARX_BADPWD); |
622 | 0 | Cmd->DllError=ERAR_BAD_PASSWORD; |
623 | 0 | ErrHandler.Exit(RARX_BADPWD); |
624 | | #else |
625 | | continue; // Request a password again. |
626 | | #endif |
627 | 0 | } |
628 | 4 | break; |
629 | 4 | } |
630 | | |
631 | 4 | Raw.SetCrypt(&HeadersCrypt); |
632 | 4 | #endif |
633 | 4 | } |
634 | | |
635 | | // Header size must not occupy more than 3 variable length integer bytes |
636 | | // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5), |
637 | | // so here we read 4 byte CRC32 followed by 3 bytes or less of header size. |
638 | 130k | const size_t FirstReadSize=7; // Smallest possible block size. |
639 | 130k | if (Raw.Read(FirstReadSize)<FirstReadSize) |
640 | 3.90k | { |
641 | 3.90k | UnexpEndArcMsg(); |
642 | 3.90k | return 0; |
643 | 3.90k | } |
644 | | |
645 | 126k | ShortBlock.Reset(); |
646 | 126k | ShortBlock.HeadCRC=Raw.Get4(); |
647 | 126k | uint SizeBytes=Raw.GetVSize(4); |
648 | 126k | uint64 BlockSize=Raw.GetV(); |
649 | | |
650 | 126k | if (BlockSize==0 || SizeBytes==0) |
651 | 1.21k | { |
652 | 1.21k | BrokenHeaderMsg(); |
653 | 1.21k | return 0; |
654 | 1.21k | } |
655 | | |
656 | 125k | int SizeToRead=int(BlockSize); |
657 | 125k | SizeToRead-=int(FirstReadSize-SizeBytes-4); // Adjust overread size bytes if any. |
658 | 125k | uint HeaderSize=4+SizeBytes+(uint)BlockSize; |
659 | | |
660 | 125k | if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5) |
661 | 233 | { |
662 | 233 | BrokenHeaderMsg(); |
663 | 233 | return 0; |
664 | 233 | } |
665 | | |
666 | 125k | Raw.Read(SizeToRead); |
667 | | |
668 | 125k | if (Raw.Size()<HeaderSize) |
669 | 644 | { |
670 | 644 | UnexpEndArcMsg(); |
671 | 644 | return 0; |
672 | 644 | } |
673 | | |
674 | 124k | uint HeaderCRC=Raw.GetCRC50(); |
675 | | |
676 | 124k | ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV(); |
677 | 124k | ShortBlock.Flags=(uint)Raw.GetV(); |
678 | 124k | ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0; |
679 | 124k | ShortBlock.HeadSize=HeaderSize; |
680 | | |
681 | 124k | CurHeaderType=ShortBlock.HeaderType; |
682 | | |
683 | 124k | bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC); |
684 | 124k | if (BadCRC) |
685 | 124k | { |
686 | 124k | BrokenHeaderMsg(); // Report, but attempt to process. |
687 | | |
688 | 124k | BrokenHeader=true; |
689 | 124k | ErrHandler.SetErrorCode(RARX_CRC); |
690 | | |
691 | 124k | if (Decrypt) |
692 | 0 | { |
693 | 0 | uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); |
694 | 0 | FailedHeaderDecryption=true; |
695 | 0 | return 0; |
696 | 0 | } |
697 | 124k | } |
698 | | |
699 | 124k | uint64 ExtraSize=0; |
700 | 124k | if ((ShortBlock.Flags & HFL_EXTRA)!=0) |
701 | 27.5k | { |
702 | 27.5k | ExtraSize=Raw.GetV(); |
703 | 27.5k | if (ExtraSize>=ShortBlock.HeadSize) |
704 | 210 | { |
705 | 210 | BrokenHeaderMsg(); |
706 | 210 | return 0; |
707 | 210 | } |
708 | 27.5k | } |
709 | | |
710 | 124k | uint64 DataSize=0; |
711 | 124k | if ((ShortBlock.Flags & HFL_DATA)!=0) |
712 | 102k | DataSize=Raw.GetV(); |
713 | | |
714 | 124k | NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); |
715 | | // Set to 0 in case of overflow, so end of ReadHeader cares about it. |
716 | 124k | NextBlockPos=SafeAdd(NextBlockPos,DataSize,0); |
717 | | |
718 | 124k | switch(ShortBlock.HeaderType) |
719 | 124k | { |
720 | 138 | case HEAD_CRYPT: |
721 | 138 | { |
722 | 138 | CryptHead.SetBaseBlock(ShortBlock); |
723 | 138 | uint CryptVersion=(uint)Raw.GetV(); |
724 | 138 | if (CryptVersion>CRYPT_VERSION) |
725 | 66 | { |
726 | 66 | UnkEncVerMsg(FileName,L"h" + std::to_wstring(CryptVersion)); |
727 | 66 | FailedHeaderDecryption=true; |
728 | 66 | return 0; |
729 | 66 | } |
730 | 72 | uint EncFlags=(uint)Raw.GetV(); |
731 | 72 | CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; |
732 | 72 | CryptHead.Lg2Count=Raw.Get1(); |
733 | 72 | if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) |
734 | 4 | { |
735 | 4 | UnkEncVerMsg(FileName,L"hc" + std::to_wstring(CryptHead.Lg2Count)); |
736 | 4 | FailedHeaderDecryption=true; |
737 | 4 | return 0; |
738 | 4 | } |
739 | | |
740 | 68 | Raw.GetB(CryptHead.Salt,SIZE_SALT50); |
741 | 68 | if (CryptHead.UsePswCheck) |
742 | 8 | { |
743 | 8 | Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); |
744 | | |
745 | 8 | byte csum[SIZE_PSWCHECK_CSUM]; |
746 | 8 | Raw.GetB(csum,SIZE_PSWCHECK_CSUM); |
747 | | |
748 | | // Exclude this code for rarext.dll, Setup.SFX and unrar_nocrypt.dll linked |
749 | | // without sha256. But still set Encrypted=true for rarext.dll here, |
750 | | // so it can recognize encrypted header archives in archive properties. |
751 | 8 | #ifndef RAR_NOCRYPT |
752 | 8 | byte Digest[SHA256_DIGEST_SIZE]; |
753 | 8 | sha256_get(CryptHead.PswCheck, SIZE_PSWCHECK, Digest); |
754 | | |
755 | 8 | CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; |
756 | 8 | #endif |
757 | 8 | } |
758 | 68 | Encrypted=true; |
759 | 68 | } |
760 | 0 | break; |
761 | 4.78k | case HEAD_MAIN: |
762 | 4.78k | { |
763 | 4.78k | MainHead.Reset(); |
764 | 4.78k | MainHead.SetBaseBlock(ShortBlock); |
765 | 4.78k | uint ArcFlags=(uint)Raw.GetV(); |
766 | | |
767 | 4.78k | Volume=(ArcFlags & MHFL_VOLUME)!=0; |
768 | 4.78k | Solid=(ArcFlags & MHFL_SOLID)!=0; |
769 | 4.78k | Locked=(ArcFlags & MHFL_LOCK)!=0; |
770 | 4.78k | Protected=(ArcFlags & MHFL_PROTECT)!=0; |
771 | 4.78k | Signed=false; |
772 | 4.78k | NewNumbering=true; |
773 | | |
774 | 4.78k | if ((ArcFlags & MHFL_VOLNUMBER)!=0) |
775 | 708 | VolNumber=(uint)Raw.GetV(); |
776 | 4.07k | else |
777 | 4.07k | VolNumber=0; |
778 | 4.78k | FirstVolume=Volume && VolNumber==0; |
779 | | |
780 | 4.78k | if (ExtraSize!=0) |
781 | 1.01k | ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); |
782 | | |
783 | 4.78k | #ifdef USE_QOPEN |
784 | 4.78k | if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) |
785 | 398 | { |
786 | | // We seek to QO block in the end of archive when processing |
787 | | // QOpen.Load, so we need to preserve current block positions |
788 | | // to not break normal archive processing by calling function. |
789 | 398 | int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; |
790 | 398 | HEADER_TYPE SaveCurHeaderType=CurHeaderType; |
791 | | |
792 | 398 | QOpen.Init(this,false); |
793 | 398 | QOpen.Load(MainHead.QOpenOffset); |
794 | | |
795 | 398 | CurBlockPos=SaveCurBlockPos; |
796 | 398 | NextBlockPos=SaveNextBlockPos; |
797 | 398 | CurHeaderType=SaveCurHeaderType; |
798 | 398 | } |
799 | 4.78k | #endif |
800 | 4.78k | } |
801 | 4.78k | break; |
802 | 78.6k | case HEAD_FILE: |
803 | 95.1k | case HEAD_SERVICE: |
804 | 95.1k | { |
805 | 95.1k | FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; |
806 | 95.1k | hd->Reset(); // Clear hash, time fields and other stuff like flags. |
807 | 95.1k | *(BaseBlock *)hd=ShortBlock; |
808 | | |
809 | 95.1k | bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; |
810 | | |
811 | 95.1k | hd->LargeFile=true; |
812 | | |
813 | 95.1k | hd->PackSize=DataSize; |
814 | 95.1k | hd->FileFlags=(uint)Raw.GetV(); |
815 | 95.1k | hd->UnpSize=Raw.GetV(); |
816 | | |
817 | 95.1k | hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0; |
818 | 95.1k | if (hd->UnknownUnpSize) |
819 | 5.47k | hd->UnpSize=INT64NDF; |
820 | | |
821 | 95.1k | hd->MaxSize=Max(hd->PackSize,hd->UnpSize); |
822 | 95.1k | hd->FileAttr=(uint)Raw.GetV(); |
823 | 95.1k | if ((hd->FileFlags & FHFL_UTIME)!=0) |
824 | 15.4k | hd->mtime.SetUnix((time_t)Raw.Get4()); |
825 | | |
826 | 95.1k | hd->FileHash.Type=HASH_NONE; |
827 | 95.1k | if ((hd->FileFlags & FHFL_CRC32)!=0) |
828 | 3.27k | { |
829 | 3.27k | hd->FileHash.Type=HASH_CRC32; |
830 | 3.27k | hd->FileHash.CRC32=Raw.Get4(); |
831 | 3.27k | } |
832 | | |
833 | 95.1k | hd->RedirType=FSREDIR_NONE; |
834 | | |
835 | 95.1k | uint CompInfo=(uint)Raw.GetV(); |
836 | 95.1k | hd->Method=(CompInfo>>7) & 7; |
837 | | |
838 | | // "+ 50" to not mix with old RAR format algorithms. For example, |
839 | | // we may need to use the compression algorithm 15 in the future, |
840 | | // but it was already used in RAR 1.5 and Unpack needs to distinguish |
841 | | // them. |
842 | 95.1k | uint UnpVer=(CompInfo & 0x3f); |
843 | 95.1k | if (UnpVer==0) |
844 | 74.9k | hd->UnpVer=VER_PACK5; |
845 | 20.1k | else |
846 | 20.1k | if (UnpVer==1) |
847 | 3.81k | hd->UnpVer=VER_PACK7; |
848 | 16.3k | else |
849 | 16.3k | hd->UnpVer=VER_UNKNOWN; |
850 | | |
851 | 95.1k | hd->HostOS=(byte)Raw.GetV(); |
852 | 95.1k | size_t NameSize=(size_t)Raw.GetV(); |
853 | 95.1k | hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0; |
854 | | |
855 | 95.1k | hd->HSType=HSYS_UNKNOWN; |
856 | 95.1k | if (hd->HostOS==HOST5_UNIX) |
857 | 4.52k | hd->HSType=HSYS_UNIX; |
858 | 90.6k | else |
859 | 90.6k | if (hd->HostOS==HOST5_WINDOWS) |
860 | 73.4k | hd->HSType=HSYS_WINDOWS; |
861 | | |
862 | 95.1k | hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0; |
863 | 95.1k | hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0; |
864 | 95.1k | hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; |
865 | 95.1k | hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; |
866 | 95.1k | hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; |
867 | 95.1k | if (hd->Dir || UnpVer>1) |
868 | 17.1k | hd->WinSize=0; |
869 | 77.9k | else |
870 | 77.9k | { |
871 | 77.9k | hd->WinSize=0x20000ULL<<((CompInfo>>10)&(UnpVer==0 ? 0x0f:0x1f)); |
872 | 77.9k | if (UnpVer==1) |
873 | 3.77k | { |
874 | 3.77k | hd->WinSize+=hd->WinSize/32*((CompInfo>>15)&0x1f); |
875 | | |
876 | | // RAR7 header with RAR5 compression. Needed to append RAR7 files |
877 | | // to RAR5 solid stream if new dictionary is larger than existing. |
878 | 3.77k | if ((CompInfo & FCI_RAR5_COMPAT)!=0) |
879 | 263 | hd->UnpVer=VER_PACK5; |
880 | 3.77k | if (hd->WinSize>UNPACK_MAX_DICT) |
881 | 24 | hd->UnpVer=VER_UNKNOWN; |
882 | 3.77k | } |
883 | 77.9k | } |
884 | | |
885 | 95.1k | size_t ReadNameSize=Min(NameSize,MAXPATHSIZE); |
886 | 95.1k | std::string FileName(ReadNameSize,0); |
887 | 95.1k | Raw.GetB((byte *)&FileName[0],ReadNameSize); |
888 | | |
889 | 95.1k | UtfToWide(FileName.data(),hd->FileName); |
890 | | |
891 | | // Should do it before converting names, because extra fields can |
892 | | // affect name processing, like in case of NTFS streams. |
893 | 95.1k | if (ExtraSize!=0) |
894 | 18.2k | ProcessExtra50(&Raw,(size_t)ExtraSize,hd); |
895 | | |
896 | 95.1k | if (FileBlock) |
897 | 78.6k | { |
898 | 78.6k | #ifndef SFX_MODULE |
899 | 78.6k | ConvertNameCase(hd->FileName); |
900 | 78.6k | #endif |
901 | 78.6k | ConvertFileHeader(hd); |
902 | 78.6k | } |
903 | | |
904 | 95.1k | if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) |
905 | 0 | MainComment=true; |
906 | | |
907 | | |
908 | 95.1k | if (BadCRC) // Add the file name to broken header message displayed above. |
909 | 95.1k | uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); |
910 | 95.1k | } |
911 | 95.1k | break; |
912 | 247 | case HEAD_ENDARC: |
913 | 247 | { |
914 | 247 | EndArcHead.SetBaseBlock(ShortBlock); |
915 | 247 | uint ArcFlags=(uint)Raw.GetV(); |
916 | 247 | EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; |
917 | 247 | EndArcHead.StoreVolNumber=false; |
918 | 247 | EndArcHead.DataCRC=false; |
919 | 247 | EndArcHead.RevSpace=false; |
920 | 247 | } |
921 | 247 | break; |
922 | 124k | } |
923 | | |
924 | 124k | return Raw.Size(); |
925 | 124k | } |
926 | | |
927 | | |
928 | | #if !defined(RAR_NOCRYPT) |
929 | | void Archive::RequestArcPassword(RarCheckPassword *CheckPwd) |
930 | 31 | { |
931 | 31 | if (!Cmd->Password.IsSet()) |
932 | 31 | { |
933 | 31 | #ifdef RARDLL |
934 | 31 | if (Cmd->Callback!=NULL) |
935 | 0 | { |
936 | 0 | wchar PasswordW[MAXPASSWORD]; |
937 | 0 | *PasswordW=0; |
938 | 0 | if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) |
939 | 0 | *PasswordW=0; |
940 | 0 | if (*PasswordW==0) |
941 | 0 | { |
942 | 0 | char PasswordA[MAXPASSWORD]; |
943 | 0 | *PasswordA=0; |
944 | 0 | if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) |
945 | 0 | *PasswordA=0; |
946 | 0 | CharToWide(PasswordA,PasswordW,ASIZE(PasswordW)); |
947 | 0 | cleandata(PasswordA,sizeof(PasswordA)); |
948 | 0 | } |
949 | 0 | Cmd->Password.Set(PasswordW); |
950 | 0 | cleandata(PasswordW,sizeof(PasswordW)); |
951 | 0 | } |
952 | 31 | if (!Cmd->Password.IsSet()) |
953 | 31 | { |
954 | 31 | Close(); |
955 | 31 | Cmd->DllError=ERAR_MISSING_PASSWORD; |
956 | 31 | ErrHandler.Exit(RARX_USERBREAK); |
957 | 31 | } |
958 | | #else |
959 | | if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd)) |
960 | | { |
961 | | Close(); |
962 | | uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. |
963 | | ErrHandler.Exit(RARX_USERBREAK); |
964 | | } |
965 | | #endif |
966 | 31 | Cmd->ManualPassword=true; |
967 | 31 | } |
968 | 31 | } |
969 | | #endif |
970 | | |
971 | | |
972 | | void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) |
973 | 19.2k | { |
974 | | // Read extra data from the end of block skipping any fields before it. |
975 | 19.2k | size_t ExtraStart=Raw->Size()-ExtraSize; |
976 | 19.2k | if (ExtraStart<Raw->GetPos()) |
977 | 12.7k | return; |
978 | 6.51k | Raw->SetPos(ExtraStart); |
979 | 44.1k | while (Raw->DataLeft()>=2) |
980 | 43.5k | { |
981 | 43.5k | int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative. |
982 | 43.5k | if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) |
983 | 5.81k | break; |
984 | 37.7k | size_t NextPos=size_t(Raw->GetPos()+FieldSize); |
985 | 37.7k | uint64 FieldType=Raw->GetV(); |
986 | | |
987 | 37.7k | FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. |
988 | | |
989 | 37.7k | if (FieldSize<0) // FieldType is longer than expected extra field size. |
990 | 179 | break; |
991 | | |
992 | 37.6k | if (bb->HeaderType==HEAD_MAIN) |
993 | 8.94k | { |
994 | 8.94k | MainHeader *hd=(MainHeader *)bb; |
995 | 8.94k | switch(FieldType) |
996 | 8.94k | { |
997 | 4.39k | case MHEXTRA_LOCATOR: |
998 | 4.39k | { |
999 | 4.39k | hd->Locator=true; |
1000 | 4.39k | uint Flags=(uint)Raw->GetV(); |
1001 | 4.39k | if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) |
1002 | 4.27k | { |
1003 | 4.27k | uint64 Offset=Raw->GetV(); |
1004 | 4.27k | if (Offset!=0) // 0 means that reserved space was not enough to write the offset. |
1005 | 4.02k | hd->QOpenOffset=Offset+CurBlockPos; |
1006 | 4.27k | } |
1007 | 4.39k | if ((Flags & MHEXTRA_LOCATOR_RR)!=0) |
1008 | 512 | { |
1009 | 512 | uint64 Offset=Raw->GetV(); |
1010 | 512 | if (Offset!=0) // 0 means that reserved space was not enough to write the offset. |
1011 | 482 | hd->RROffset=Offset+CurBlockPos; |
1012 | 512 | } |
1013 | 4.39k | } |
1014 | 4.39k | break; |
1015 | 1.99k | case MHEXTRA_METADATA: |
1016 | 1.99k | { |
1017 | 1.99k | uint Flags=(uint)Raw->GetV(); |
1018 | 1.99k | if ((Flags & MHEXTRA_METADATA_NAME)!=0) |
1019 | 184 | { |
1020 | 184 | uint64 NameSize=Raw->GetV(); |
1021 | 184 | if (NameSize>0 && NameSize<MAXPATHSIZE) // Prevent excessive allocation. |
1022 | 129 | { |
1023 | 129 | std::string NameU((size_t)NameSize,0); // UTF-8 name. |
1024 | 129 | Raw->GetB(&NameU[0],(size_t)NameSize); |
1025 | | // If starts from 0, the name was longer than reserved space |
1026 | | // when saving this extra field. |
1027 | 129 | if (NameU[0]!=0) |
1028 | 118 | UtfToWide(&NameU[0],hd->OrigName); |
1029 | 129 | } |
1030 | 184 | } |
1031 | 1.99k | if ((Flags & MHEXTRA_METADATA_CTIME)!=0) |
1032 | 1.84k | if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0) |
1033 | 120 | if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0) |
1034 | 56 | hd->OrigTime.SetUnixNS(Raw->Get8()); |
1035 | 64 | else |
1036 | 64 | hd->OrigTime.SetUnix((time_t)Raw->Get4()); |
1037 | 1.72k | else |
1038 | 1.72k | hd->OrigTime.SetWin(Raw->Get8()); |
1039 | 1.99k | } |
1040 | 1.99k | break; |
1041 | 8.94k | } |
1042 | 8.94k | } |
1043 | | |
1044 | 37.6k | if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) |
1045 | 28.6k | { |
1046 | 28.6k | FileHeader *hd=(FileHeader *)bb; |
1047 | 28.6k | switch(FieldType) |
1048 | 28.6k | { |
1049 | 0 | #ifndef RAR_NOCRYPT // Except rarext.dll, Setup.SFX and unrar_nocrypt.dll. |
1050 | 2.31k | case FHEXTRA_CRYPT: |
1051 | 2.31k | { |
1052 | 2.31k | FileHeader *hd=(FileHeader *)bb; |
1053 | 2.31k | uint EncVersion=(uint)Raw->GetV(); |
1054 | 2.31k | if (EncVersion>CRYPT_VERSION) |
1055 | 2.15k | { |
1056 | 2.15k | UnkEncVerMsg(hd->FileName,L"x" + std::to_wstring(EncVersion)); |
1057 | 2.15k | hd->CryptMethod=CRYPT_UNKNOWN; |
1058 | 2.15k | } |
1059 | 161 | else |
1060 | 161 | { |
1061 | 161 | uint Flags=(uint)Raw->GetV(); |
1062 | 161 | hd->Lg2Count=Raw->Get1(); |
1063 | 161 | if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) |
1064 | 28 | { |
1065 | 28 | UnkEncVerMsg(hd->FileName,L"xc" + std::to_wstring(hd->Lg2Count)); |
1066 | 28 | hd->CryptMethod=CRYPT_UNKNOWN; |
1067 | 28 | } |
1068 | 133 | else |
1069 | 133 | { |
1070 | 133 | hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; |
1071 | 133 | hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; |
1072 | | |
1073 | 133 | Raw->GetB(hd->Salt,SIZE_SALT50); |
1074 | 133 | Raw->GetB(hd->InitV,SIZE_INITV); |
1075 | 133 | if (hd->UsePswCheck) |
1076 | 104 | { |
1077 | 104 | Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); |
1078 | | |
1079 | | // It is important to know if password check data is valid. |
1080 | | // If it is damaged and header CRC32 fails to detect it, |
1081 | | // archiver would refuse to decompress a possibly valid file. |
1082 | | // Since we want to be sure distinguishing a wrong password |
1083 | | // or corrupt file data, we use 64-bit password check data |
1084 | | // and to control its validity we use 32 bits of password |
1085 | | // check data SHA-256 additionally to 32-bit header CRC32. |
1086 | 104 | byte csum[SIZE_PSWCHECK_CSUM]; |
1087 | 104 | Raw->GetB(csum,SIZE_PSWCHECK_CSUM); |
1088 | | |
1089 | 104 | byte Digest[SHA256_DIGEST_SIZE]; |
1090 | 104 | sha256_get(hd->PswCheck, SIZE_PSWCHECK, Digest); |
1091 | | |
1092 | 104 | hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; |
1093 | | |
1094 | | // RAR 5.21 and earlier set PswCheck field in service records to 0 |
1095 | | // even if UsePswCheck was present. |
1096 | 104 | if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) |
1097 | 5 | hd->UsePswCheck=0; |
1098 | 104 | } |
1099 | 133 | hd->SaltSet=true; |
1100 | 133 | hd->CryptMethod=CRYPT_RAR50; |
1101 | 133 | hd->Encrypted=true; |
1102 | 133 | } |
1103 | 161 | } |
1104 | 2.31k | } |
1105 | 2.31k | break; |
1106 | 0 | #endif |
1107 | 3.25k | case FHEXTRA_HASH: |
1108 | 3.25k | { |
1109 | 3.25k | FileHeader *hd=(FileHeader *)bb; |
1110 | 3.25k | uint Type=(uint)Raw->GetV(); |
1111 | 3.25k | if (Type==FHEXTRA_HASH_BLAKE2) |
1112 | 516 | { |
1113 | 516 | hd->FileHash.Type=HASH_BLAKE2; |
1114 | 516 | Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); |
1115 | 516 | } |
1116 | 3.25k | } |
1117 | 3.25k | break; |
1118 | 11.1k | case FHEXTRA_HTIME: |
1119 | 11.1k | if (FieldSize>=5) |
1120 | 1.08k | { |
1121 | 1.08k | byte Flags=(byte)Raw->GetV(); |
1122 | 1.08k | bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; |
1123 | 1.08k | if ((Flags & FHEXTRA_HTIME_MTIME)!=0) |
1124 | 1.00k | if (UnixTime) |
1125 | 750 | hd->mtime.SetUnix(Raw->Get4()); |
1126 | 252 | else |
1127 | 252 | hd->mtime.SetWin(Raw->Get8()); |
1128 | 1.08k | if ((Flags & FHEXTRA_HTIME_CTIME)!=0) |
1129 | 435 | if (UnixTime) |
1130 | 191 | hd->ctime.SetUnix(Raw->Get4()); |
1131 | 244 | else |
1132 | 244 | hd->ctime.SetWin(Raw->Get8()); |
1133 | 1.08k | if ((Flags & FHEXTRA_HTIME_ATIME)!=0) |
1134 | 245 | if (UnixTime) |
1135 | 193 | hd->atime.SetUnix((time_t)Raw->Get4()); |
1136 | 52 | else |
1137 | 52 | hd->atime.SetWin(Raw->Get8()); |
1138 | 1.08k | if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. |
1139 | 214 | { |
1140 | 214 | uint ns; |
1141 | 214 | if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) |
1142 | 165 | hd->mtime.Adjust(ns); |
1143 | 214 | if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) |
1144 | 156 | hd->ctime.Adjust(ns); |
1145 | 214 | if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) |
1146 | 157 | hd->atime.Adjust(ns); |
1147 | 214 | } |
1148 | 1.08k | } |
1149 | 11.1k | break; |
1150 | 873 | case FHEXTRA_VERSION: |
1151 | 873 | if (FieldSize>=1) |
1152 | 811 | { |
1153 | 811 | Raw->GetV(); // Skip flags field. |
1154 | 811 | uint Version=(uint)Raw->GetV(); |
1155 | 811 | if (Version!=0) |
1156 | 771 | { |
1157 | 771 | hd->Version=true; |
1158 | 771 | hd->FileName += L';' + std::to_wstring(Version); |
1159 | 771 | } |
1160 | 811 | } |
1161 | 873 | break; |
1162 | 2.16k | case FHEXTRA_REDIR: |
1163 | 2.16k | { |
1164 | 2.16k | FILE_SYSTEM_REDIRECT RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); |
1165 | 2.16k | uint Flags=(uint)Raw->GetV(); |
1166 | 2.16k | size_t NameSize=(size_t)Raw->GetV(); |
1167 | | |
1168 | 2.16k | if (NameSize>0 && NameSize<MAXPATHSIZE) |
1169 | 2.02k | { |
1170 | 2.02k | std::string UtfName(NameSize,0); |
1171 | 2.02k | hd->RedirType=RedirType; |
1172 | 2.02k | hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; |
1173 | 2.02k | Raw->GetB(&UtfName[0],NameSize); |
1174 | 2.02k | UtfToWide(&UtfName[0],hd->RedirName); |
1175 | | #ifdef _WIN_ALL |
1176 | | UnixSlashToDos(hd->RedirName,hd->RedirName); |
1177 | | #endif |
1178 | 2.02k | } |
1179 | 2.16k | } |
1180 | 2.16k | break; |
1181 | 3.55k | case FHEXTRA_UOWNER: |
1182 | 3.55k | { |
1183 | 3.55k | uint Flags=(uint)Raw->GetV(); |
1184 | 3.55k | hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0; |
1185 | 3.55k | hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0; |
1186 | 3.55k | *hd->UnixOwnerName=*hd->UnixGroupName=0; |
1187 | 3.55k | if ((Flags & FHEXTRA_UOWNER_UNAME)!=0) |
1188 | 468 | { |
1189 | 468 | size_t Length=(size_t)Raw->GetV(); |
1190 | 468 | Length=Min(Length,ASIZE(hd->UnixOwnerName)-1); |
1191 | 468 | Raw->GetB(hd->UnixOwnerName,Length); |
1192 | 468 | hd->UnixOwnerName[Length]=0; |
1193 | 468 | } |
1194 | 3.55k | if ((Flags & FHEXTRA_UOWNER_GNAME)!=0) |
1195 | 3.46k | { |
1196 | 3.46k | size_t Length=(size_t)Raw->GetV(); |
1197 | 3.46k | Length=Min(Length,ASIZE(hd->UnixGroupName)-1); |
1198 | 3.46k | Raw->GetB(hd->UnixGroupName,Length); |
1199 | 3.46k | hd->UnixGroupName[Length]=0; |
1200 | 3.46k | } |
1201 | 3.55k | #ifdef _UNIX |
1202 | 3.55k | if (hd->UnixOwnerNumeric) |
1203 | 3.14k | hd->UnixOwnerID=(uid_t)Raw->GetV(); |
1204 | 3.55k | if (hd->UnixGroupNumeric) |
1205 | 104 | hd->UnixGroupID=(gid_t)Raw->GetV(); |
1206 | | #else |
1207 | | // Need these fields in Windows too for 'list' command, |
1208 | | // but uid_t and gid_t are not defined. |
1209 | | if (hd->UnixOwnerNumeric) |
1210 | | hd->UnixOwnerID=(uint)Raw->GetV(); |
1211 | | if (hd->UnixGroupNumeric) |
1212 | | hd->UnixGroupID=(uint)Raw->GetV(); |
1213 | | #endif |
1214 | 3.55k | hd->UnixOwnerSet=true; |
1215 | 3.55k | } |
1216 | 3.55k | break; |
1217 | 602 | case FHEXTRA_SUBDATA: |
1218 | 602 | { |
1219 | | // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than |
1220 | | // required. It did not hurt extraction, because UnRAR 5.21 |
1221 | | // and earlier ignored this field and set FieldSize as data left |
1222 | | // in entire extra area. But now we set the correct field size |
1223 | | // and set FieldSize based on the actual extra record size, |
1224 | | // so we need to adjust it for those older archives here. |
1225 | | // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE |
1226 | | // and always is last in extra area. So since its size is by 1 |
1227 | | // less than needed, we always have 1 byte left in extra area, |
1228 | | // which fact we use here to detect such archives. |
1229 | 602 | if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) |
1230 | 5 | FieldSize++; |
1231 | | |
1232 | | // We cannot allocate too much memory here, because above |
1233 | | // we check FieldSize againt Raw size and we control that Raw size |
1234 | | // is sensible when reading headers. |
1235 | 602 | hd->SubData.resize((size_t)FieldSize); |
1236 | 602 | Raw->GetB(hd->SubData.data(),(size_t)FieldSize); |
1237 | 602 | } |
1238 | 602 | break; |
1239 | 28.6k | } |
1240 | 28.6k | } |
1241 | | |
1242 | 37.6k | Raw->SetPos(NextPos); |
1243 | 37.6k | } |
1244 | 6.51k | } |
1245 | | |
1246 | | |
1247 | | #ifndef SFX_MODULE |
1248 | | size_t Archive::ReadHeader14() |
1249 | 6.13k | { |
1250 | 6.13k | RawRead Raw(this); |
1251 | 6.13k | if (CurBlockPos<=(int64)SFXSize) |
1252 | 1.91k | { |
1253 | 1.91k | Raw.Read(SIZEOF_MAINHEAD14); |
1254 | 1.91k | MainHead.Reset(); |
1255 | 1.91k | byte Mark[4]; |
1256 | 1.91k | Raw.GetB(Mark,4); |
1257 | 1.91k | uint HeadSize=Raw.Get2(); |
1258 | 1.91k | if (HeadSize<7) |
1259 | 879 | return 0; |
1260 | 1.03k | byte Flags=Raw.Get1(); |
1261 | 1.03k | NextBlockPos=CurBlockPos+HeadSize; |
1262 | 1.03k | CurHeaderType=HEAD_MAIN; |
1263 | | |
1264 | 1.03k | Volume=(Flags & MHD_VOLUME)!=0; |
1265 | 1.03k | Solid=(Flags & MHD_SOLID)!=0; |
1266 | 1.03k | Locked=(Flags & MHD_LOCK)!=0; |
1267 | 1.03k | MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0; |
1268 | 1.03k | MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0; |
1269 | 1.03k | } |
1270 | 4.22k | else |
1271 | 4.22k | { |
1272 | 4.22k | Raw.Read(SIZEOF_FILEHEAD14); |
1273 | 4.22k | FileHead.Reset(); |
1274 | | |
1275 | 4.22k | FileHead.HeaderType=HEAD_FILE; |
1276 | 4.22k | FileHead.DataSize=Raw.Get4(); |
1277 | 4.22k | FileHead.UnpSize=Raw.Get4(); |
1278 | 4.22k | FileHead.FileHash.Type=HASH_RAR14; |
1279 | 4.22k | FileHead.FileHash.CRC32=Raw.Get2(); |
1280 | 4.22k | FileHead.HeadSize=Raw.Get2(); |
1281 | 4.22k | if (FileHead.HeadSize<21) |
1282 | 2.54k | return 0; |
1283 | 1.67k | uint FileTime=Raw.Get4(); |
1284 | 1.67k | FileHead.FileAttr=Raw.Get1(); |
1285 | 1.67k | FileHead.Flags=Raw.Get1()|LONG_BLOCK; |
1286 | 1.67k | FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10; |
1287 | 1.67k | size_t NameSize=Raw.Get1(); |
1288 | 1.67k | FileHead.Method=Raw.Get1(); |
1289 | | |
1290 | 1.67k | FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0; |
1291 | 1.67k | FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0; |
1292 | 1.67k | FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0; |
1293 | 1.67k | FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; |
1294 | | |
1295 | 1.67k | FileHead.PackSize=FileHead.DataSize; |
1296 | 1.67k | FileHead.WinSize=0x10000; |
1297 | 1.67k | FileHead.Dir=(FileHead.FileAttr & 0x10)!=0; |
1298 | | |
1299 | 1.67k | FileHead.HostOS=HOST_MSDOS; |
1300 | 1.67k | FileHead.HSType=HSYS_WINDOWS; |
1301 | | |
1302 | 1.67k | FileHead.mtime.SetDos(FileTime); |
1303 | | |
1304 | 1.67k | Raw.Read(NameSize); |
1305 | | |
1306 | | // RAR 1.4 name size is stored in a single byte field and it can't |
1307 | | // exceed 255, so additional checks are not needed. |
1308 | 1.67k | std::string FileName(NameSize,0); |
1309 | 1.67k | Raw.GetB((byte *)&FileName[0],NameSize); |
1310 | 1.67k | std::string NameA; |
1311 | 1.67k | IntToExt(FileName,NameA); |
1312 | 1.67k | CharToWide(NameA,FileHead.FileName); |
1313 | 1.67k | ConvertNameCase(FileHead.FileName); |
1314 | 1.67k | ConvertFileHeader(&FileHead); |
1315 | | |
1316 | 1.67k | if (Raw.Size()!=0) |
1317 | 1.67k | NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; |
1318 | 1.67k | CurHeaderType=HEAD_FILE; |
1319 | 1.67k | } |
1320 | 2.71k | return NextBlockPos>CurBlockPos ? Raw.Size() : 0; |
1321 | 6.13k | } |
1322 | | #endif |
1323 | | |
1324 | | |
1325 | | #ifndef SFX_MODULE |
1326 | | void Archive::ConvertNameCase(std::wstring &Name) |
1327 | 85.3k | { |
1328 | 85.3k | if (Cmd->ConvertNames==NAMES_UPPERCASE) |
1329 | 0 | wcsupper(Name); |
1330 | 85.3k | if (Cmd->ConvertNames==NAMES_LOWERCASE) |
1331 | 0 | wcslower(Name); |
1332 | 85.3k | } |
1333 | | #endif |
1334 | | |
1335 | | |
1336 | | bool Archive::IsArcDir() |
1337 | 159k | { |
1338 | 159k | return FileHead.Dir; |
1339 | 159k | } |
1340 | | |
1341 | | |
1342 | | void Archive::ConvertAttributes() |
1343 | 80.8k | { |
1344 | | #ifdef _WIN_ALL |
1345 | | if (FileHead.HSType!=HSYS_WINDOWS) |
1346 | | FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; |
1347 | | #endif |
1348 | 80.8k | #ifdef _UNIX |
1349 | | // umask defines which permission bits must not be set by default |
1350 | | // when creating a file or directory. The typical default value |
1351 | | // for the process umask is S_IWGRP | S_IWOTH (octal 022), |
1352 | | // resulting in 0644 mode for new files. |
1353 | | // Normally umask is applied automatically when creating a file, |
1354 | | // but we set attributes with chmod later, so we need to calculate |
1355 | | // resulting attributes here. We do it only for non-Unix archives. |
1356 | | // We restore native Unix attributes as is, because it can be backup. |
1357 | 80.8k | static mode_t mask = (mode_t) -1; |
1358 | | |
1359 | 80.8k | if (mask == (mode_t) -1) |
1360 | 1 | { |
1361 | | // umask call returns the current umask value. Argument (022) is not |
1362 | | // really important here. |
1363 | 1 | mask = umask(022); |
1364 | | |
1365 | | // Restore the original umask value, which was changed to 022 above. |
1366 | 1 | umask(mask); |
1367 | 1 | } |
1368 | | |
1369 | 80.8k | switch(FileHead.HSType) |
1370 | 80.8k | { |
1371 | 60.5k | case HSYS_WINDOWS: |
1372 | 60.5k | { |
1373 | | // Mapping MSDOS, OS/2 and Windows file attributes to Unix. |
1374 | | |
1375 | 60.5k | if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY |
1376 | 541 | { |
1377 | | // For directories we use 0777 mask. |
1378 | 541 | FileHead.FileAttr=0777 & ~mask; |
1379 | 541 | } |
1380 | 60.0k | else |
1381 | 60.0k | if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY |
1382 | 139 | { |
1383 | | // For read only files we use 0444 mask with 'w' bits turned off. |
1384 | 139 | FileHead.FileAttr=0444 & ~mask; |
1385 | 139 | } |
1386 | 59.9k | else |
1387 | 59.9k | { |
1388 | | // umask does not set +x for regular files, so we use 0666 |
1389 | | // instead of 0777 as for directories. |
1390 | 59.9k | FileHead.FileAttr=0666 & ~mask; |
1391 | 59.9k | } |
1392 | 60.5k | } |
1393 | 60.5k | break; |
1394 | 3.02k | case HSYS_UNIX: |
1395 | 3.02k | break; |
1396 | 17.2k | default: |
1397 | 17.2k | if (FileHead.Dir) |
1398 | 1.57k | FileHead.FileAttr=0x41ff & ~mask; |
1399 | 15.6k | else |
1400 | 15.6k | FileHead.FileAttr=0x81b6 & ~mask; |
1401 | 17.2k | break; |
1402 | 80.8k | } |
1403 | 80.8k | #endif |
1404 | 80.8k | } |
1405 | | |
1406 | | |
1407 | | void Archive::ConvertFileHeader(FileHeader *hd) |
1408 | 85.3k | { |
1409 | | /* |
1410 | | if (hd->HSType==HSYS_UNKNOWN) |
1411 | | if (hd->Dir) |
1412 | | hd->FileAttr=0x10; |
1413 | | else |
1414 | | hd->FileAttr=0x20; |
1415 | | */ |
1416 | | |
1417 | | #ifdef _WIN_ALL |
1418 | | if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. |
1419 | | ConvertToPrecomposed(hd->FileName); |
1420 | | #endif |
1421 | | |
1422 | 24.7M | for (uint I=0;I<hd->FileName.size();I++) |
1423 | 24.6M | { |
1424 | 24.6M | wchar *s=&hd->FileName[I]; |
1425 | | |
1426 | 24.6M | #ifdef _UNIX |
1427 | | // Backslash is the invalid character for Windows file headers, |
1428 | | // but it can present in Unix file names extracted in Unix. |
1429 | 24.6M | if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) |
1430 | 110 | *s='_'; |
1431 | 24.6M | #endif |
1432 | | |
1433 | | #ifdef _WIN_ALL |
1434 | | // RAR 5.0 archives do not use '\' as path separator, so if we see it, |
1435 | | // it means that it is a part of Unix file name, which we cannot |
1436 | | // extract in Windows. |
1437 | | if (*s=='\\' && Format==RARFMT50) |
1438 | | *s='_'; |
1439 | | |
1440 | | // ':' in file names is allowed in Unix, but not in Windows. |
1441 | | // Even worse, file data will be written to NTFS stream on NTFS, |
1442 | | // so automatic name correction on file create error in extraction |
1443 | | // routine does not work. In Windows and DOS versions we better |
1444 | | // replace ':' now. |
1445 | | if (*s==':') |
1446 | | *s='_'; |
1447 | | #endif |
1448 | | |
1449 | | // This code must be performed only after other path separator checks, |
1450 | | // because it produces backslashes illegal for some of checks above. |
1451 | | // Backslash is allowed in file names in Unix, but not in Windows. |
1452 | | // Still, RAR 4.x uses backslashes as path separator even in Unix. |
1453 | | // Forward slash is not allowed in both systems. In RAR 5.0 we use |
1454 | | // the forward slash as universal path separator. |
1455 | 24.6M | if (*s=='/' || *s=='\\' && Format!=RARFMT50) |
1456 | 73.6k | *s=CPATHDIVIDER; |
1457 | 24.6M | } |
1458 | | |
1459 | | // Zeroes inside might be possible in broken Unicode names decoded with EncodeFileName::Decode. |
1460 | 85.3k | TruncateAtZero(hd->FileName); // Ensure there are no zeroes inside of string. |
1461 | 85.3k | } |
1462 | | |
1463 | | |
1464 | | int64 Archive::GetStartPos() |
1465 | 105 | { |
1466 | 105 | int64 StartPos=SFXSize+MarkHead.HeadSize; |
1467 | 105 | if (Format==RARFMT15) |
1468 | 105 | StartPos+=MainHead.HeadSize; |
1469 | 0 | else // RAR 5.0. |
1470 | 0 | StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); |
1471 | 105 | return StartPos; |
1472 | 105 | } |
1473 | | |
1474 | | |
1475 | | bool Archive::ReadSubData(std::vector<byte> *UnpData,File *DestFile,bool TestMode) |
1476 | 0 | { |
1477 | 0 | if (BrokenHeader) |
1478 | 0 | { |
1479 | 0 | uiMsg(UIERROR_SUBHEADERBROKEN,FileName); |
1480 | 0 | ErrHandler.SetErrorCode(RARX_CRC); |
1481 | 0 | return false; |
1482 | 0 | } |
1483 | 0 | if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK7:VER_UNPACK)) |
1484 | 0 | { |
1485 | 0 | uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); |
1486 | 0 | return false; |
1487 | 0 | } |
1488 | | |
1489 | 0 | if (SubHead.PackSize==0 && !SubHead.SplitAfter) |
1490 | 0 | return true; |
1491 | | |
1492 | 0 | SubDataIO.Init(); |
1493 | 0 | Unpack Unpack(&SubDataIO); |
1494 | 0 | Unpack.Init(SubHead.WinSize,false); |
1495 | |
|
1496 | 0 | if (DestFile==NULL) |
1497 | 0 | { |
1498 | 0 | if (SubHead.UnpSize>0x1000000) |
1499 | 0 | { |
1500 | | // Prevent the excessive allocation. When reading to memory, normally |
1501 | | // this function operates with reasonably small blocks, such as |
1502 | | // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream. |
1503 | 0 | uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); |
1504 | 0 | return false; |
1505 | 0 | } |
1506 | 0 | if (UnpData==NULL) |
1507 | 0 | SubDataIO.SetTestMode(true); |
1508 | 0 | else |
1509 | 0 | { |
1510 | 0 | UnpData->resize((size_t)SubHead.UnpSize); |
1511 | 0 | SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); |
1512 | 0 | } |
1513 | 0 | } |
1514 | 0 | if (SubHead.Encrypted) |
1515 | 0 | if (Cmd->Password.IsSet()) |
1516 | 0 | SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, |
1517 | 0 | SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, |
1518 | 0 | SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); |
1519 | 0 | else |
1520 | 0 | return false; |
1521 | 0 | SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); |
1522 | 0 | SubDataIO.SetPackedSizeToRead(SubHead.PackSize); |
1523 | 0 | SubDataIO.EnableShowProgress(false); |
1524 | 0 | SubDataIO.SetFiles(this,DestFile); |
1525 | 0 | SubDataIO.SetTestMode(TestMode); |
1526 | 0 | SubDataIO.UnpVolume=SubHead.SplitAfter; |
1527 | 0 | SubDataIO.SetSubHeader(&SubHead,NULL); |
1528 | 0 | Unpack.SetDestSize(SubHead.UnpSize); |
1529 | 0 | if (SubHead.Method==0) |
1530 | 0 | CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); |
1531 | 0 | else |
1532 | 0 | Unpack.DoUnpack(SubHead.UnpVer,false); |
1533 | |
|
1534 | 0 | if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) |
1535 | 0 | { |
1536 | 0 | uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); |
1537 | 0 | ErrHandler.SetErrorCode(RARX_CRC); |
1538 | 0 | if (UnpData!=NULL) |
1539 | 0 | UnpData->clear(); |
1540 | 0 | return false; |
1541 | 0 | } |
1542 | 0 | return true; |
1543 | 0 | } |