Line | Count | Source (jump to first uncovered line) |
1 | | #include "rar.hpp" |
2 | | |
3 | | #include "arccmt.cpp" |
4 | | |
5 | | |
6 | | Archive::Archive(CommandData *InitCmd) |
7 | 2.68k | { |
8 | 2.68k | Cmd=NULL; // Just in case we'll have an exception in 'new' below. |
9 | | |
10 | 2.68k | DummyCmd=(InitCmd==NULL); |
11 | 2.68k | Cmd=DummyCmd ? (new CommandData):InitCmd; |
12 | | |
13 | 2.68k | OpenShared=Cmd->OpenShared; |
14 | 2.68k | Format=RARFMT_NONE; |
15 | 2.68k | Solid=false; |
16 | 2.68k | Volume=false; |
17 | 2.68k | MainComment=false; |
18 | 2.68k | Locked=false; |
19 | 2.68k | Signed=false; |
20 | 2.68k | FirstVolume=false; |
21 | 2.68k | NewNumbering=false; |
22 | 2.68k | SFXSize=0; |
23 | 2.68k | LatestTime.Reset(); |
24 | 2.68k | Protected=false; |
25 | 2.68k | Encrypted=false; |
26 | 2.68k | FailedHeaderDecryption=false; |
27 | 2.68k | BrokenHeader=false; |
28 | 2.68k | LastReadBlock=0; |
29 | | |
30 | 2.68k | CurBlockPos=0; |
31 | 2.68k | NextBlockPos=0; |
32 | | |
33 | | |
34 | 2.68k | MainHead.Reset(); |
35 | 2.68k | CryptHead={}; |
36 | 2.68k | EndArcHead.Reset(); |
37 | | |
38 | 2.68k | VolNumber=0; |
39 | 2.68k | VolWrite=0; |
40 | 2.68k | AddingFilesSize=0; |
41 | 2.68k | AddingHeadersSize=0; |
42 | | |
43 | 2.68k | Splitting=false; |
44 | 2.68k | NewArchive=false; |
45 | | |
46 | 2.68k | SilentOpen=false; |
47 | | |
48 | 2.68k | #ifdef USE_QOPEN |
49 | 2.68k | ProhibitQOpen=false; |
50 | 2.68k | #endif |
51 | | |
52 | 2.68k | } |
53 | | |
54 | | |
55 | | Archive::~Archive() |
56 | 2.68k | { |
57 | 2.68k | if (DummyCmd) |
58 | 0 | delete Cmd; |
59 | 2.68k | } |
60 | | |
61 | | |
62 | | void Archive::CheckArc(bool EnableBroken) |
63 | 0 | { |
64 | 0 | if (!IsArchive(EnableBroken)) |
65 | 0 | { |
66 | | // If FailedHeaderDecryption is set, we already reported that archive |
67 | | // password is incorrect. |
68 | 0 | if (!FailedHeaderDecryption) |
69 | 0 | uiMsg(UIERROR_BADARCHIVE,FileName); |
70 | 0 | ErrHandler.Exit(RARX_FATAL); |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | |
75 | | #if !defined(SFX_MODULE) |
76 | | void Archive::CheckOpen(const std::wstring &Name) |
77 | 0 | { |
78 | 0 | TOpen(Name); |
79 | 0 | CheckArc(false); |
80 | 0 | } |
81 | | #endif |
82 | | |
83 | | |
84 | | bool Archive::WCheckOpen(const std::wstring &Name) |
85 | 0 | { |
86 | 0 | if (!WOpen(Name)) |
87 | 0 | return false; |
88 | 0 | if (!IsArchive(false)) |
89 | 0 | { |
90 | 0 | uiMsg(UIERROR_BADARCHIVE,FileName); |
91 | 0 | Close(); |
92 | 0 | return false; |
93 | 0 | } |
94 | 0 | return true; |
95 | 0 | } |
96 | | |
97 | | |
98 | | RARFORMAT Archive::IsSignature(const byte *D,size_t Size) |
99 | 19.1k | { |
100 | 19.1k | RARFORMAT Type=RARFMT_NONE; |
101 | 19.1k | if (Size>=1 && D[0]==0x52) |
102 | 17.8k | #ifndef SFX_MODULE |
103 | 17.8k | if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) |
104 | 1.75k | Type=RARFMT14; |
105 | 16.1k | else |
106 | 16.1k | #endif |
107 | 16.1k | if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) |
108 | 2.26k | { |
109 | | // We check the last signature byte, so we can return a sensible |
110 | | // warning in case we'll want to change the archive format |
111 | | // sometimes in the future. |
112 | 2.26k | if (D[6]==0) |
113 | 1.23k | Type=RARFMT15; |
114 | 1.03k | else |
115 | 1.03k | if (D[6]==1) |
116 | 893 | Type=RARFMT50; |
117 | 140 | else |
118 | 140 | if (D[6]>1 && D[6]<5) |
119 | 3 | Type=RARFMT_FUTURE; |
120 | 2.26k | } |
121 | 19.1k | return Type; |
122 | 19.1k | } |
123 | | |
124 | | |
125 | | bool Archive::IsArchive(bool EnableBroken) |
126 | 2.68k | { |
127 | 2.68k | Encrypted=false; |
128 | 2.68k | BrokenHeader=false; // Might be left from previous volume. |
129 | | |
130 | 2.68k | #ifndef SFX_MODULE |
131 | 2.68k | if (IsDevice()) |
132 | 0 | { |
133 | 0 | uiMsg(UIERROR_INVALIDNAME,FileName,FileName); |
134 | 0 | return false; |
135 | 0 | } |
136 | 2.68k | #endif |
137 | 2.68k | if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) |
138 | 3 | return false; |
139 | 2.68k | SFXSize=0; |
140 | | |
141 | 2.68k | RARFORMAT Type; |
142 | 2.68k | if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) |
143 | 864 | { |
144 | 864 | Format=Type; |
145 | 864 | if (Format==RARFMT14) |
146 | 464 | Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); |
147 | 864 | } |
148 | 1.82k | else |
149 | 1.82k | { |
150 | 1.82k | std::vector<char> Buffer(MAXSFXSIZE); |
151 | 1.82k | long CurPos=(long)Tell(); |
152 | 1.82k | int ReadSize=Read(Buffer.data(),Buffer.size()-16); |
153 | 4.64M | for (int I=0;I<ReadSize;I++) |
154 | 4.64M | if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE) |
155 | 3.01k | { |
156 | 3.01k | Format=Type; |
157 | 3.01k | if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31) |
158 | 1.25k | { |
159 | 1.25k | char *D=&Buffer[28-CurPos]; |
160 | 1.25k | if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) |
161 | 1.25k | continue; |
162 | 1.25k | } |
163 | 1.75k | SFXSize=CurPos+I; |
164 | 1.75k | Seek(SFXSize,SEEK_SET); |
165 | 1.75k | if (Format==RARFMT15 || Format==RARFMT50) |
166 | 1.73k | Read(MarkHead.Mark,SIZEOF_MARKHEAD3); |
167 | 1.75k | break; |
168 | 3.01k | } |
169 | 1.82k | if (SFXSize==0) |
170 | 62 | return false; |
171 | 1.82k | } |
172 | 2.62k | if (Format==RARFMT_FUTURE) |
173 | 3 | { |
174 | 3 | uiMsg(UIERROR_NEWRARFORMAT,FileName); |
175 | 3 | return false; |
176 | 3 | } |
177 | 2.62k | if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. |
178 | 893 | { |
179 | 893 | if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) |
180 | 1 | return false; |
181 | 892 | MarkHead.HeadSize=SIZEOF_MARKHEAD5; |
182 | 892 | } |
183 | 1.72k | else |
184 | 1.72k | MarkHead.HeadSize=SIZEOF_MARKHEAD3; |
185 | | |
186 | 2.61k | #ifdef RARDLL |
187 | | // If callback function is not set, we cannot get the password, |
188 | | // so we skip the initial header processing for encrypted header archive. |
189 | | // It leads to skipped archive comment, but the rest of archive data |
190 | | // is processed correctly. |
191 | 2.61k | if (Cmd->Callback==NULL) |
192 | 2.61k | SilentOpen=true; |
193 | 2.61k | #endif |
194 | | |
195 | 2.61k | bool HeadersLeft; // Any headers left to read. |
196 | 2.61k | bool StartFound=false; // Main or encryption headers found. |
197 | | // Skip the archive encryption header if any and read the main header. |
198 | 4.02k | while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang. |
199 | 2.51k | { |
200 | 2.51k | SeekToNext(); |
201 | | |
202 | 2.51k | HEADER_TYPE Type=GetHeaderType(); |
203 | | // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to |
204 | | // avoid the password prompt. |
205 | 2.51k | StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT; |
206 | 2.51k | if (StartFound) |
207 | 1.11k | break; |
208 | 2.51k | } |
209 | | |
210 | | |
211 | | // We should not do it for EnableBroken or we'll get 'not RAR archive' |
212 | | // messages when extracting encrypted archives with wrong password. |
213 | 2.61k | if (FailedHeaderDecryption && !EnableBroken) |
214 | 0 | return false; |
215 | | |
216 | 2.61k | if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing. |
217 | 2.30k | { |
218 | 2.30k | if (!FailedHeaderDecryption) // If not reported a wrong password already. |
219 | 2.30k | uiMsg(UIERROR_MHEADERBROKEN,FileName); |
220 | 2.30k | if (!EnableBroken) |
221 | 0 | return false; |
222 | 2.30k | } |
223 | | |
224 | 2.61k | MainComment=MainHead.CommentInHeader; |
225 | | |
226 | | // If we process non-encrypted archive or can request a password, |
227 | | // we set 'first volume' flag based on file attributes below. |
228 | | // It is necessary for RAR 2.x archives, which did not have 'first volume' |
229 | | // flag in main header. Also for all RAR formats we need to scan until |
230 | | // first file header to set "comment" flag when reading service header. |
231 | | // Unless we are in silent mode, we need to know about presence of comment |
232 | | // immediately after IsArchive call. |
233 | 2.61k | if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable()) |
234 | 1.10k | { |
235 | 1.10k | int64 SavePos=Tell(); |
236 | 1.10k | int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; |
237 | 1.10k | HEADER_TYPE SaveCurHeaderType=CurHeaderType; |
238 | | |
239 | 2.14k | while (ReadHeader()!=0) |
240 | 1.90k | { |
241 | 1.90k | HEADER_TYPE HeaderType=GetHeaderType(); |
242 | 1.90k | if (HeaderType==HEAD_SERVICE) |
243 | 513 | { |
244 | | // If we have a split service headers, it surely indicates non-first |
245 | | // volume. But not split service header does not guarantee the first |
246 | | // volume, because we can have split file after non-split archive |
247 | | // comment. So we do not quit from loop here. |
248 | 513 | FirstVolume=Volume && !SubHead.SplitBefore; |
249 | 513 | } |
250 | 1.39k | else |
251 | 1.39k | if (HeaderType==HEAD_FILE) |
252 | 858 | { |
253 | 858 | FirstVolume=Volume && !FileHead.SplitBefore; |
254 | 858 | break; |
255 | 858 | } |
256 | 533 | else |
257 | 533 | if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. |
258 | 6 | break; |
259 | 1.04k | SeekToNext(); |
260 | 1.04k | } |
261 | 1.10k | CurBlockPos=SaveCurBlockPos; |
262 | 1.10k | NextBlockPos=SaveNextBlockPos; |
263 | 1.10k | CurHeaderType=SaveCurHeaderType; |
264 | 1.10k | Seek(SavePos,SEEK_SET); |
265 | 1.10k | } |
266 | 2.61k | if (!Volume || FirstVolume) |
267 | 2.51k | FirstVolumeName=FileName; |
268 | | |
269 | 2.61k | return true; |
270 | 2.61k | } |
271 | | |
272 | | |
273 | | |
274 | | |
275 | | void Archive::SeekToNext() |
276 | 38.5k | { |
277 | 38.5k | Seek(NextBlockPos,SEEK_SET); |
278 | 38.5k | } |
279 | | |
280 | | |
281 | | |
282 | | |
283 | | |
284 | | |
285 | | // Calculate the block size including encryption fields and padding if any. |
286 | | uint Archive::FullHeaderSize(size_t Size) |
287 | 38.8k | { |
288 | 38.8k | if (Encrypted) |
289 | 0 | { |
290 | 0 | Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. |
291 | 0 | if (Format == RARFMT50) |
292 | 0 | Size += SIZE_INITV; |
293 | 0 | else |
294 | 0 | Size += SIZE_SALT30; |
295 | 0 | } |
296 | 38.8k | return uint(Size); |
297 | 38.8k | } |
298 | | |
299 | | |
300 | | |
301 | | |
302 | | #ifdef USE_QOPEN |
303 | | bool Archive::Open(const std::wstring &Name,uint Mode) |
304 | 2.68k | { |
305 | | // Important if we reuse Archive object and it has virtual QOpen |
306 | | // file position not matching real. For example, for 'l -v volname'. |
307 | 2.68k | QOpen.Unload(); |
308 | | |
309 | 2.68k | return File::Open(Name,Mode); |
310 | 2.68k | } |
311 | | |
312 | | |
313 | | int Archive::Read(void *Data,size_t Size) |
314 | 136k | { |
315 | 136k | size_t Result; |
316 | 136k | if (QOpen.Read(Data,Size,Result)) |
317 | 0 | return (int)Result; |
318 | 136k | return File::Read(Data,Size); |
319 | 136k | } |
320 | | |
321 | | |
322 | | void Archive::Seek(int64 Offset,int Method) |
323 | 78.7k | { |
324 | 78.7k | if (!QOpen.Seek(Offset,Method)) |
325 | 78.7k | File::Seek(Offset,Method); |
326 | 78.7k | } |
327 | | |
328 | | |
329 | | int64 Archive::Tell() |
330 | 55.1k | { |
331 | 55.1k | int64 QPos; |
332 | 55.1k | if (QOpen.Tell(&QPos)) |
333 | 0 | return QPos; |
334 | 55.1k | return File::Tell(); |
335 | 55.1k | } |
336 | | #endif |
337 | | |
338 | | |
339 | | // Return 0 if dictionary size is invalid. If size is RAR7 only, return |
340 | | // the adjusted nearest bottom value. Return header flags in Flags. |
341 | | uint64 Archive::GetWinSize(uint64 Size,uint &Flags) |
342 | 0 | { |
343 | 0 | Flags=0; |
344 | | // Allow 128 KB - 1 TB range. |
345 | 0 | if (Size<0x20000 || Size>0x10000000000ULL) |
346 | 0 | return 0; |
347 | 0 | uint64 Pow2=0x20000; // Power of 2 dictionary size. |
348 | 0 | for (;2*Pow2<=Size;Pow2*=2) |
349 | 0 | Flags+=FCI_DICT_BIT0; |
350 | 0 | if (Size==Pow2) |
351 | 0 | return Size; // If 'Size' is the power of 2, return it as is. |
352 | | |
353 | | // Get the number of Pow2/32 to add to Pow2 for nearest value not exceeding 'Size'. |
354 | 0 | uint64 Fraction=(Size-Pow2)/(Pow2/32); |
355 | 0 | Flags+=(uint)Fraction*FCI_DICT_FRACT0; |
356 | 0 | return Pow2+Fraction*(Pow2/32); |
357 | 0 | } |