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