view src/win32/7zip/7z/CPP/7zip/Archive/Rar/RarIn.cpp @ 1:f9f4f1b99eed

importing src directory
author Robert McIntyre <rlm@mit.edu>
date Sat, 03 Mar 2012 10:31:27 -0600
parents
children
line wrap: on
line source
1 // Archive/RarIn.cpp
3 #include "StdAfx.h"
5 #include "Common/StringConvert.h"
6 #include "Common/UTFConvert.h"
8 #include "RarIn.h"
9 #include "../../Common/LimitedStreams.h"
10 #include "../../Common/StreamUtils.h"
12 #include "../Common/FindSignature.h"
14 extern "C"
15 {
16 #include "../../../../C/7zCrc.h"
17 }
19 namespace NArchive {
20 namespace NRar {
22 void CInArchive::ThrowExceptionWithCode(
23 CInArchiveException::CCauseType cause)
24 {
25 throw CInArchiveException(cause);
26 }
28 HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit)
29 {
30 try
31 {
32 Close();
33 HRESULT res = Open2(inStream, searchHeaderSizeLimit);
34 if (res == S_OK)
35 return res;
36 Close();
37 return res;
38 }
39 catch(...) { Close(); throw; }
40 }
42 void CInArchive::Close()
43 {
44 m_Stream.Release();
45 }
48 static inline bool TestMarkerCandidate(const void *aTestBytes)
49 {
50 for (UInt32 i = 0; i < NHeader::kMarkerSize; i++)
51 if (((const Byte *)aTestBytes)[i] != NHeader::kMarker[i])
52 return false;
53 return true;
54 }
56 HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
57 {
58 RINOK(FindSignatureInStream(stream,
59 NHeader::kMarker, NHeader::kMarkerSize,
60 searchHeaderSizeLimit, m_ArchiveStartPosition));
61 m_Stream = stream;
62 m_Position = m_ArchiveStartPosition + NHeader::kMarkerSize;
63 return m_Stream->Seek(m_Position, STREAM_SEEK_SET, NULL);
64 }
66 void CInArchive::ThrowUnexpectedEndOfArchiveException()
67 {
68 ThrowExceptionWithCode(CInArchiveException::kUnexpectedEndOfArchive);
69 }
71 bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
72 {
73 if (m_CryptoMode)
74 {
75 const Byte *bufData = (const Byte *)m_DecryptedData;
76 UInt32 bufSize = m_DecryptedDataSize;
77 UInt32 i;
78 for (i = 0; i < size && m_CryptoPos < bufSize; i++)
79 ((Byte *)data)[i] = bufData[m_CryptoPos++];
80 return (i == size);
81 }
82 return (ReadStream_FALSE(m_Stream, data, size) == S_OK);
83 }
85 void CInArchive::ReadBytesAndTestResult(void *data, UInt32 size)
86 {
87 if(!ReadBytesAndTestSize(data,size))
88 ThrowUnexpectedEndOfArchiveException();
89 }
91 HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
92 {
93 size_t realProcessedSize = size;
94 HRESULT result = ReadStream(m_Stream, data, &realProcessedSize);
95 if (processedSize != NULL)
96 *processedSize = (UInt32)realProcessedSize;
97 AddToSeekValue(realProcessedSize);
98 return result;
99 }
101 static UInt32 CrcUpdateUInt16(UInt32 crc, UInt16 v)
102 {
103 crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));
104 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));
105 return crc;
106 }
108 static UInt32 CrcUpdateUInt32(UInt32 crc, UInt32 v)
109 {
110 crc = CRC_UPDATE_BYTE(crc, (Byte)(v & 0xFF));
111 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 8) & 0xFF));
112 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 16) & 0xFF));
113 crc = CRC_UPDATE_BYTE(crc, (Byte)((v >> 24) & 0xFF));
114 return crc;
115 }
118 HRESULT CInArchive::Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
119 {
120 m_CryptoMode = false;
121 RINOK(stream->Seek(0, STREAM_SEEK_SET, &m_StreamStartPosition));
122 m_Position = m_StreamStartPosition;
124 RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));
126 Byte buf[NHeader::NArchive::kArchiveHeaderSize];
127 UInt32 processedSize;
128 ReadBytes(buf, sizeof(buf), &processedSize);
129 if (processedSize != sizeof(buf))
130 return S_FALSE;
131 m_CurData = buf;
132 m_CurPos = 0;
133 m_PosLimit = sizeof(buf);
135 m_ArchiveHeader.CRC = ReadUInt16();
136 m_ArchiveHeader.Type = ReadByte();
137 m_ArchiveHeader.Flags = ReadUInt16();
138 m_ArchiveHeader.Size = ReadUInt16();
139 m_ArchiveHeader.Reserved1 = ReadUInt16();
140 m_ArchiveHeader.Reserved2 = ReadUInt32();
141 m_ArchiveHeader.EncryptVersion = 0;
143 UInt32 crc = CRC_INIT_VAL;
144 crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.Type);
145 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Flags);
146 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Size);
147 crc = CrcUpdateUInt16(crc, m_ArchiveHeader.Reserved1);
148 crc = CrcUpdateUInt32(crc, m_ArchiveHeader.Reserved2);
150 if (m_ArchiveHeader.IsThereEncryptVer() && m_ArchiveHeader.Size > NHeader::NArchive::kArchiveHeaderSize)
151 {
152 ReadBytes(&m_ArchiveHeader.EncryptVersion, 1, &processedSize);
153 if (processedSize != 1)
154 return S_FALSE;
155 crc = CRC_UPDATE_BYTE(crc, m_ArchiveHeader.EncryptVersion);
156 }
158 if(m_ArchiveHeader.CRC != (CRC_GET_DIGEST(crc) & 0xFFFF))
159 ThrowExceptionWithCode(CInArchiveException::kArchiveHeaderCRCError);
160 if (m_ArchiveHeader.Type != NHeader::NBlockType::kArchiveHeader)
161 return S_FALSE;
162 m_ArchiveCommentPosition = m_Position;
163 m_SeekOnArchiveComment = true;
164 return S_OK;
165 }
167 void CInArchive::SkipArchiveComment()
168 {
169 if (!m_SeekOnArchiveComment)
170 return;
171 AddToSeekValue(m_ArchiveHeader.Size - m_ArchiveHeader.GetBaseSize());
172 m_SeekOnArchiveComment = false;
173 }
175 void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const
176 {
177 archiveInfo.StartPosition = m_ArchiveStartPosition;
178 archiveInfo.Flags = m_ArchiveHeader.Flags;
179 archiveInfo.CommentPosition = m_ArchiveCommentPosition;
180 archiveInfo.CommentSize = (UInt16)(m_ArchiveHeader.Size - NHeader::NArchive::kArchiveHeaderSize);
181 }
183 static void DecodeUnicodeFileName(const char *name, const Byte *encName,
184 int encSize, wchar_t *unicodeName, int maxDecSize)
185 {
186 int encPos = 0;
187 int decPos = 0;
188 int flagBits = 0;
189 Byte flags = 0;
190 Byte highByte = encName[encPos++];
191 while (encPos < encSize && decPos < maxDecSize)
192 {
193 if (flagBits == 0)
194 {
195 flags = encName[encPos++];
196 flagBits = 8;
197 }
198 switch(flags >> 6)
199 {
200 case 0:
201 unicodeName[decPos++] = encName[encPos++];
202 break;
203 case 1:
204 unicodeName[decPos++] = (wchar_t)(encName[encPos++] + (highByte << 8));
205 break;
206 case 2:
207 unicodeName[decPos++] = (wchar_t)(encName[encPos] + (encName[encPos + 1] << 8));
208 encPos += 2;
209 break;
210 case 3:
211 {
212 int length = encName[encPos++];
213 if (length & 0x80)
214 {
215 Byte correction = encName[encPos++];
216 for (length = (length & 0x7f) + 2;
217 length > 0 && decPos < maxDecSize; length--, decPos++)
218 unicodeName[decPos] = (wchar_t)(((name[decPos] + correction) & 0xff) + (highByte << 8));
219 }
220 else
221 for (length += 2; length > 0 && decPos < maxDecSize; length--, decPos++)
222 unicodeName[decPos] = name[decPos];
223 }
224 break;
225 }
226 flags <<= 2;
227 flagBits -= 2;
228 }
229 unicodeName[decPos < maxDecSize ? decPos : maxDecSize - 1] = 0;
230 }
232 void CInArchive::ReadName(CItemEx &item, int nameSize)
233 {
234 item.UnicodeName.Empty();
235 if (nameSize > 0)
236 {
237 m_NameBuffer.EnsureCapacity(nameSize + 1);
238 char *buffer = (char *)m_NameBuffer;
240 for (int i = 0; i < nameSize; i++)
241 buffer[i] = ReadByte();
243 int mainLen;
244 for (mainLen = 0; mainLen < nameSize; mainLen++)
245 if (buffer[mainLen] == '\0')
246 break;
247 buffer[mainLen] = '\0';
248 item.Name = buffer;
250 if(item.HasUnicodeName())
251 {
252 if(mainLen < nameSize)
253 {
254 int unicodeNameSizeMax = MyMin(nameSize, (0x400));
255 _unicodeNameBuffer.EnsureCapacity(unicodeNameSizeMax + 1);
256 DecodeUnicodeFileName(buffer, (const Byte *)buffer + mainLen + 1,
257 nameSize - (mainLen + 1), _unicodeNameBuffer, unicodeNameSizeMax);
258 item.UnicodeName = _unicodeNameBuffer;
259 }
260 else if (!ConvertUTF8ToUnicode(item.Name, item.UnicodeName))
261 item.UnicodeName.Empty();
262 }
263 }
264 else
265 item.Name.Empty();
266 }
268 Byte CInArchive::ReadByte()
269 {
270 if (m_CurPos >= m_PosLimit)
271 throw CInArchiveException(CInArchiveException::kIncorrectArchive);
272 return m_CurData[m_CurPos++];
273 }
275 UInt16 CInArchive::ReadUInt16()
276 {
277 UInt16 value = 0;
278 for (int i = 0; i < 2; i++)
279 {
280 Byte b = ReadByte();
281 value |= (UInt16(b) << (8 * i));
282 }
283 return value;
284 }
286 UInt32 CInArchive::ReadUInt32()
287 {
288 UInt32 value = 0;
289 for (int i = 0; i < 4; i++)
290 {
291 Byte b = ReadByte();
292 value |= (UInt32(b) << (8 * i));
293 }
294 return value;
295 }
297 void CInArchive::ReadTime(Byte mask, CRarTime &rarTime)
298 {
299 rarTime.LowSecond = (Byte)(((mask & 4) != 0) ? 1 : 0);
300 int numDigits = (mask & 3);
301 rarTime.SubTime[0] = rarTime.SubTime[1] = rarTime.SubTime[2] = 0;
302 for (int i = 0; i < numDigits; i++)
303 rarTime.SubTime[3 - numDigits + i] = ReadByte();
304 }
306 void CInArchive::ReadHeaderReal(CItemEx &item)
307 {
308 item.Flags = m_BlockHeader.Flags;
309 item.PackSize = ReadUInt32();
310 item.Size = ReadUInt32();
311 item.HostOS = ReadByte();
312 item.FileCRC = ReadUInt32();
313 item.MTime.DosTime = ReadUInt32();
314 item.UnPackVersion = ReadByte();
315 item.Method = ReadByte();
316 int nameSize = ReadUInt16();
317 item.Attrib = ReadUInt32();
319 item.MTime.LowSecond = 0;
320 item.MTime.SubTime[0] =
321 item.MTime.SubTime[1] =
322 item.MTime.SubTime[2] = 0;
324 if((item.Flags & NHeader::NFile::kSize64Bits) != 0)
325 {
326 item.PackSize |= ((UInt64)ReadUInt32() << 32);
327 item.Size |= ((UInt64)ReadUInt32() << 32);
328 }
330 ReadName(item, nameSize);
332 if (item.HasSalt())
333 for (int i = 0; i < sizeof(item.Salt); i++)
334 item.Salt[i] = ReadByte();
336 // some rar archives have HasExtTime flag without field.
337 if (m_CurPos < m_PosLimit && item.HasExtTime())
338 {
339 Byte accessMask = (Byte)(ReadByte() >> 4);
340 Byte b = ReadByte();
341 Byte modifMask = (Byte)(b >> 4);
342 Byte createMask = (Byte)(b & 0xF);
343 if ((modifMask & 8) != 0)
344 ReadTime(modifMask, item.MTime);
345 item.CTimeDefined = ((createMask & 8) != 0);
346 if (item.CTimeDefined)
347 {
348 item.CTime.DosTime = ReadUInt32();
349 ReadTime(createMask, item.CTime);
350 }
351 item.ATimeDefined = ((accessMask & 8) != 0);
352 if (item.ATimeDefined)
353 {
354 item.ATime.DosTime = ReadUInt32();
355 ReadTime(accessMask, item.ATime);
356 }
357 }
359 UInt16 fileHeaderWithNameSize = (UInt16)m_CurPos;
361 item.Position = m_Position;
362 item.MainPartSize = fileHeaderWithNameSize;
363 item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize);
365 if (m_CryptoMode)
366 item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF);
367 else
368 item.AlignSize = 0;
369 AddToSeekValue(m_BlockHeader.HeadSize);
370 }
372 void CInArchive::AddToSeekValue(UInt64 addValue)
373 {
374 m_Position += addValue;
375 }
377 HRESULT CInArchive::GetNextItem(CItemEx &item, ICryptoGetTextPassword *getTextPassword)
378 {
379 if (m_SeekOnArchiveComment)
380 SkipArchiveComment();
381 for (;;)
382 {
383 if(!SeekInArchive(m_Position))
384 return S_FALSE;
385 if (!m_CryptoMode && (m_ArchiveHeader.Flags &
386 NHeader::NArchive::kBlockHeadersAreEncrypted) != 0)
387 {
388 m_CryptoMode = false;
389 if (getTextPassword == 0)
390 return S_FALSE;
391 if(!SeekInArchive(m_Position))
392 return S_FALSE;
393 if (!m_RarAES)
394 {
395 m_RarAESSpec = new NCrypto::NRar29::CDecoder;
396 m_RarAES = m_RarAESSpec;
397 }
398 m_RarAESSpec->SetRar350Mode(m_ArchiveHeader.IsEncryptOld());
400 // Salt
401 const UInt32 kSaltSize = 8;
402 Byte salt[kSaltSize];
403 if(!ReadBytesAndTestSize(salt, kSaltSize))
404 return S_FALSE;
405 m_Position += kSaltSize;
406 RINOK(m_RarAESSpec->SetDecoderProperties2(salt, kSaltSize))
407 // Password
408 CMyComBSTR password;
409 RINOK(getTextPassword->CryptoGetTextPassword(&password))
410 UString unicodePassword(password);
412 CByteBuffer buffer;
413 const UInt32 sizeInBytes = unicodePassword.Length() * 2;
414 buffer.SetCapacity(sizeInBytes);
415 for (int i = 0; i < unicodePassword.Length(); i++)
416 {
417 wchar_t c = unicodePassword[i];
418 ((Byte *)buffer)[i * 2] = (Byte)c;
419 ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
420 }
422 RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
424 const UInt32 kDecryptedBufferSize = (1 << 12);
425 if (m_DecryptedData.GetCapacity() == 0)
426 {
427 m_DecryptedData.SetCapacity(kDecryptedBufferSize);
428 }
429 RINOK(m_RarAES->Init());
430 size_t decryptedDataSizeT = kDecryptedBufferSize;
431 RINOK(ReadStream(m_Stream, (Byte *)m_DecryptedData, &decryptedDataSizeT));
432 m_DecryptedDataSize = (UInt32)decryptedDataSizeT;
433 m_DecryptedDataSize = m_RarAES->Filter((Byte *)m_DecryptedData, m_DecryptedDataSize);
435 m_CryptoMode = true;
436 m_CryptoPos = 0;
437 }
439 m_FileHeaderData.EnsureCapacity(7);
440 if(!ReadBytesAndTestSize((Byte *)m_FileHeaderData, 7))
441 return S_FALSE;
443 m_CurData = (Byte *)m_FileHeaderData;
444 m_CurPos = 0;
445 m_PosLimit = 7;
446 m_BlockHeader.CRC = ReadUInt16();
447 m_BlockHeader.Type = ReadByte();
448 m_BlockHeader.Flags = ReadUInt16();
449 m_BlockHeader.HeadSize = ReadUInt16();
451 if (m_BlockHeader.HeadSize < 7)
452 ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive);
454 if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive)
455 return S_FALSE;
457 if (m_BlockHeader.Type == NHeader::NBlockType::kFileHeader)
458 {
459 m_FileHeaderData.EnsureCapacity(m_BlockHeader.HeadSize);
460 m_CurData = (Byte *)m_FileHeaderData;
461 m_PosLimit = m_BlockHeader.HeadSize;
462 ReadBytesAndTestResult(m_CurData + m_CurPos, m_BlockHeader.HeadSize - 7);
463 ReadHeaderReal(item);
464 if ((CrcCalc(m_CurData + 2,
465 m_BlockHeader.HeadSize - item.CommentSize - 2) & 0xFFFF) != m_BlockHeader.CRC)
466 ThrowExceptionWithCode(CInArchiveException::kFileHeaderCRCError);
468 FinishCryptoBlock();
469 m_CryptoMode = false;
470 SeekInArchive(m_Position); // Move Position to compressed Data;
471 AddToSeekValue(item.PackSize); // m_Position points to next header;
472 return S_OK;
473 }
474 if (m_CryptoMode && m_BlockHeader.HeadSize > (1 << 12))
475 return E_FAIL; // it's for bad passwords
476 if ((m_BlockHeader.Flags & NHeader::NBlock::kLongBlock) != 0)
477 {
478 m_FileHeaderData.EnsureCapacity(7 + 4);
479 m_CurData = (Byte *)m_FileHeaderData;
480 ReadBytesAndTestResult(m_CurData + m_CurPos, 4); // test it
481 m_PosLimit = 7 + 4;
482 UInt32 dataSize = ReadUInt32();
483 AddToSeekValue(dataSize);
484 if (m_CryptoMode && dataSize > (1 << 27))
485 return E_FAIL; // it's for bad passwords
486 m_CryptoPos = m_BlockHeader.HeadSize;
487 }
488 else
489 m_CryptoPos = 0;
490 AddToSeekValue(m_BlockHeader.HeadSize);
491 FinishCryptoBlock();
492 m_CryptoMode = false;
493 }
494 }
496 bool CInArchive::SeekInArchive(UInt64 position)
497 {
498 UInt64 newPosition;
499 m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition);
500 return newPosition == position;
501 }
503 ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size)
504 {
505 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
506 CMyComPtr<ISequentialInStream> inStream(streamSpec);
507 SeekInArchive(position);
508 streamSpec->SetStream(m_Stream);
509 streamSpec->Init(size);
510 return inStream.Detach();
511 }
513 }}