1 // Archive/IsoIn.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/CpuArch.h"
6
7 #include "../../../Common/MyException.h"
8
9 #include "../../Common/StreamUtils.h"
10
11 #include "../HandlerCont.h"
12
13 #include "IsoIn.h"
14
15 namespace NArchive {
16 namespace NIso {
17
18 struct CUnexpectedEndException {};
19 struct CHeaderErrorException {};
20 struct CEndianErrorException {};
21
22 static const char * const kMediaTypes[] =
23 {
24 "NoEmul"
25 , "1.2M"
26 , "1.44M"
27 , "2.88M"
28 , "HardDisk"
29 };
30
Parse(const Byte * p)31 bool CBootInitialEntry::Parse(const Byte *p)
32 {
33 Bootable = (p[0] == NBootEntryId::kInitialEntryBootable);
34 BootMediaType = p[1];
35 LoadSegment = GetUi16(p + 2);
36 SystemType = p[4];
37 SectorCount = GetUi16(p + 6);
38 LoadRBA = GetUi32(p + 8);
39 memcpy(VendorSpec, p + 12, 20);
40 if (p[5] != 0)
41 return false;
42 if (p[0] != NBootEntryId::kInitialEntryBootable
43 && p[0] != NBootEntryId::kInitialEntryNotBootable)
44 return false;
45 return true;
46 }
47
GetName() const48 AString CBootInitialEntry::GetName() const
49 {
50 AString s (Bootable ? "Boot" : "NotBoot");
51 s += '-';
52
53 if (BootMediaType < ARRAY_SIZE(kMediaTypes))
54 s += kMediaTypes[BootMediaType];
55 else
56 s.Add_UInt32(BootMediaType);
57
58 if (VendorSpec[0] == 1)
59 {
60 // "Language and Version Information (IBM)"
61
62 unsigned i;
63 for (i = 1; i < sizeof(VendorSpec); i++)
64 if (VendorSpec[i] > 0x7F)
65 break;
66 if (i == sizeof(VendorSpec))
67 {
68 s += '-';
69 for (i = 1; i < sizeof(VendorSpec); i++)
70 {
71 char c = VendorSpec[i];
72 if (c == 0)
73 break;
74 if (c == '\\' || c == '/')
75 c = '_';
76 s += c;
77 }
78 }
79 }
80
81 s += ".img";
82 return s;
83 }
84
ReadByte()85 Byte CInArchive::ReadByte()
86 {
87 if (m_BufferPos >= kBlockSize)
88 m_BufferPos = 0;
89 if (m_BufferPos == 0)
90 {
91 size_t processed = kBlockSize;
92 HRESULT res = ReadStream(_stream, m_Buffer, &processed);
93 if (res != S_OK)
94 throw CSystemException(res);
95 if (processed != kBlockSize)
96 throw CUnexpectedEndException();
97 UInt64 end = _position + processed;
98 if (PhySize < end)
99 PhySize = end;
100 }
101 Byte b = m_Buffer[m_BufferPos++];
102 _position++;
103 return b;
104 }
105
ReadBytes(Byte * data,UInt32 size)106 void CInArchive::ReadBytes(Byte *data, UInt32 size)
107 {
108 for (UInt32 i = 0; i < size; i++)
109 data[i] = ReadByte();
110 }
111
Skip(size_t size)112 void CInArchive::Skip(size_t size)
113 {
114 while (size-- != 0)
115 ReadByte();
116 }
117
SkipZeros(size_t size)118 void CInArchive::SkipZeros(size_t size)
119 {
120 while (size-- != 0)
121 {
122 Byte b = ReadByte();
123 if (b != 0)
124 throw CHeaderErrorException();
125 }
126 }
127
ReadUInt16()128 UInt16 CInArchive::ReadUInt16()
129 {
130 Byte b[4];
131 ReadBytes(b, 4);
132 UInt32 val = 0;
133 for (int i = 0; i < 2; i++)
134 {
135 if (b[i] != b[3 - i])
136 IncorrectBigEndian = true;
137 val |= ((UInt16)(b[i]) << (8 * i));
138 }
139 return (UInt16)val;
140 }
141
ReadUInt32Le()142 UInt32 CInArchive::ReadUInt32Le()
143 {
144 UInt32 val = 0;
145 for (int i = 0; i < 4; i++)
146 val |= ((UInt32)(ReadByte()) << (8 * i));
147 return val;
148 }
149
ReadUInt32Be()150 UInt32 CInArchive::ReadUInt32Be()
151 {
152 UInt32 val = 0;
153 for (int i = 0; i < 4; i++)
154 {
155 val <<= 8;
156 val |= ReadByte();
157 }
158 return val;
159 }
160
ReadUInt32()161 UInt32 CInArchive::ReadUInt32()
162 {
163 Byte b[8];
164 ReadBytes(b, 8);
165 UInt32 val = 0;
166 for (int i = 0; i < 4; i++)
167 {
168 if (b[i] != b[7 - i])
169 throw CEndianErrorException();
170 val |= ((UInt32)(b[i]) << (8 * i));
171 }
172 return val;
173 }
174
ReadDigits(int numDigits)175 UInt32 CInArchive::ReadDigits(int numDigits)
176 {
177 UInt32 res = 0;
178 for (int i = 0; i < numDigits; i++)
179 {
180 Byte b = ReadByte();
181 if (b < '0' || b > '9')
182 {
183 if (b == 0 || b == ' ') // it's bug in some CD's
184 b = '0';
185 else
186 throw CHeaderErrorException();
187 }
188 UInt32 d = (UInt32)(b - '0');
189 res *= 10;
190 res += d;
191 }
192 return res;
193 }
194
ReadDateTime(CDateTime & d)195 void CInArchive::ReadDateTime(CDateTime &d)
196 {
197 d.Year = (UInt16)ReadDigits(4);
198 d.Month = (Byte)ReadDigits(2);
199 d.Day = (Byte)ReadDigits(2);
200 d.Hour = (Byte)ReadDigits(2);
201 d.Minute = (Byte)ReadDigits(2);
202 d.Second = (Byte)ReadDigits(2);
203 d.Hundredths = (Byte)ReadDigits(2);
204 d.GmtOffset = (signed char)ReadByte();
205 }
206
ReadBootRecordDescriptor(CBootRecordDescriptor & d)207 void CInArchive::ReadBootRecordDescriptor(CBootRecordDescriptor &d)
208 {
209 ReadBytes(d.BootSystemId, sizeof(d.BootSystemId));
210 ReadBytes(d.BootId, sizeof(d.BootId));
211 ReadBytes(d.BootSystemUse, sizeof(d.BootSystemUse));
212 }
213
ReadRecordingDateTime(CRecordingDateTime & t)214 void CInArchive::ReadRecordingDateTime(CRecordingDateTime &t)
215 {
216 t.Year = ReadByte();
217 t.Month = ReadByte();
218 t.Day = ReadByte();
219 t.Hour = ReadByte();
220 t.Minute = ReadByte();
221 t.Second = ReadByte();
222 t.GmtOffset = (signed char)ReadByte();
223 }
224
ReadDirRecord2(CDirRecord & r,Byte len)225 void CInArchive::ReadDirRecord2(CDirRecord &r, Byte len)
226 {
227 r.ExtendedAttributeRecordLen = ReadByte();
228 if (r.ExtendedAttributeRecordLen != 0)
229 throw CHeaderErrorException();
230 r.ExtentLocation = ReadUInt32();
231 r.Size = ReadUInt32();
232 ReadRecordingDateTime(r.DateTime);
233 r.FileFlags = ReadByte();
234 r.FileUnitSize = ReadByte();
235 r.InterleaveGapSize = ReadByte();
236 r.VolSequenceNumber = ReadUInt16();
237 Byte idLen = ReadByte();
238 r.FileId.Alloc(idLen);
239 ReadBytes((Byte *)r.FileId, idLen);
240 unsigned padSize = 1 - (idLen & 1);
241
242 // SkipZeros(padSize);
243 Skip(padSize); // it's bug in some cd's. Must be zeros
244
245 unsigned curPos = 33 + idLen + padSize;
246 if (curPos > len)
247 throw CHeaderErrorException();
248 unsigned rem = len - curPos;
249 r.SystemUse.Alloc(rem);
250 ReadBytes((Byte *)r.SystemUse, rem);
251 }
252
ReadDirRecord(CDirRecord & r)253 void CInArchive::ReadDirRecord(CDirRecord &r)
254 {
255 Byte len = ReadByte();
256 // Some CDs can have incorrect value len = 48 ('0') in VolumeDescriptor.
257 // But maybe we must use real "len" for other records.
258 len = 34;
259 ReadDirRecord2(r, len);
260 }
261
ReadVolumeDescriptor(CVolumeDescriptor & d)262 void CInArchive::ReadVolumeDescriptor(CVolumeDescriptor &d)
263 {
264 d.VolFlags = ReadByte();
265 ReadBytes(d.SystemId, sizeof(d.SystemId));
266 ReadBytes(d.VolumeId, sizeof(d.VolumeId));
267 SkipZeros(8);
268 d.VolumeSpaceSize = ReadUInt32();
269 ReadBytes(d.EscapeSequence, sizeof(d.EscapeSequence));
270 d.VolumeSetSize = ReadUInt16();
271 d.VolumeSequenceNumber = ReadUInt16();
272 d.LogicalBlockSize = ReadUInt16();
273 d.PathTableSize = ReadUInt32();
274 d.LPathTableLocation = ReadUInt32Le();
275 d.LOptionalPathTableLocation = ReadUInt32Le();
276 d.MPathTableLocation = ReadUInt32Be();
277 d.MOptionalPathTableLocation = ReadUInt32Be();
278 ReadDirRecord(d.RootDirRecord);
279 ReadBytes(d.VolumeSetId, sizeof(d.VolumeSetId));
280 ReadBytes(d.PublisherId, sizeof(d.PublisherId));
281 ReadBytes(d.DataPreparerId, sizeof(d.DataPreparerId));
282 ReadBytes(d.ApplicationId, sizeof(d.ApplicationId));
283 ReadBytes(d.CopyrightFileId, sizeof(d.CopyrightFileId));
284 ReadBytes(d.AbstractFileId, sizeof(d.AbstractFileId));
285 ReadBytes(d.BibFileId, sizeof(d.BibFileId));
286 ReadDateTime(d.CTime);
287 ReadDateTime(d.MTime);
288 ReadDateTime(d.ExpirationTime);
289 ReadDateTime(d.EffectiveTime);
290 d.FileStructureVersion = ReadByte(); // = 1
291 SkipZeros(1);
292 ReadBytes(d.ApplicationUse, sizeof(d.ApplicationUse));
293
294 // Most ISO contains zeros in the following field (reserved for future standardization).
295 // But some ISO programs write some data to that area.
296 // So we disable check for zeros.
297 Skip(653); // SkipZeros(653);
298 }
299
300 static const Byte kSig_CD001[5] = { 'C', 'D', '0', '0', '1' };
301
302 /*
303 static const Byte kSig_NSR02[5] = { 'N', 'S', 'R', '0', '2' };
304 static const Byte kSig_NSR03[5] = { 'N', 'S', 'R', '0', '3' };
305 static const Byte kSig_BEA01[5] = { 'B', 'E', 'A', '0', '1' };
306 static const Byte kSig_TEA01[5] = { 'T', 'E', 'A', '0', '1' };
307 */
308
CheckSignature(const Byte * sig,const Byte * data)309 static inline bool CheckSignature(const Byte *sig, const Byte *data)
310 {
311 for (int i = 0; i < 5; i++)
312 if (sig[i] != data[i])
313 return false;
314 return true;
315 }
316
SeekToBlock(UInt32 blockIndex)317 void CInArchive::SeekToBlock(UInt32 blockIndex)
318 {
319 HRESULT res = _stream->Seek((UInt64)blockIndex * VolDescs[MainVolDescIndex].LogicalBlockSize, STREAM_SEEK_SET, &_position);
320 if (res != S_OK)
321 throw CSystemException(res);
322 m_BufferPos = 0;
323 }
324
325 static const int kNumLevelsMax = 256;
326
ReadDir(CDir & d,int level)327 void CInArchive::ReadDir(CDir &d, int level)
328 {
329 if (!d.IsDir())
330 return;
331 if (level > kNumLevelsMax)
332 {
333 TooDeepDirs = true;
334 return;
335 }
336
337 {
338 FOR_VECTOR (i, UniqStartLocations)
339 if (UniqStartLocations[i] == d.ExtentLocation)
340 {
341 SelfLinkedDirs = true;
342 return;
343 }
344 UniqStartLocations.Add(d.ExtentLocation);
345 }
346
347 SeekToBlock(d.ExtentLocation);
348 UInt64 startPos = _position;
349
350 bool firstItem = true;
351 for (;;)
352 {
353 UInt64 offset = _position - startPos;
354 if (offset >= d.Size)
355 break;
356 Byte len = ReadByte();
357 if (len == 0)
358 continue;
359 CDir subItem;
360 ReadDirRecord2(subItem, len);
361 if (firstItem && level == 0)
362 IsSusp = subItem.CheckSusp(SuspSkipSize);
363
364 if (!subItem.IsSystemItem())
365 d._subItems.Add(subItem);
366
367 firstItem = false;
368 }
369 FOR_VECTOR (i, d._subItems)
370 ReadDir(d._subItems[i], level + 1);
371
372 UniqStartLocations.DeleteBack();
373 }
374
CreateRefs(CDir & d)375 void CInArchive::CreateRefs(CDir &d)
376 {
377 if (!d.IsDir())
378 return;
379 for (unsigned i = 0; i < d._subItems.Size();)
380 {
381 CRef ref;
382 CDir &subItem = d._subItems[i];
383 subItem.Parent = &d;
384 ref.Dir = &d;
385 ref.Index = i++;
386 ref.NumExtents = 1;
387 ref.TotalSize = subItem.Size;
388 if (subItem.IsNonFinalExtent())
389 {
390 for (;;)
391 {
392 if (i == d._subItems.Size())
393 {
394 HeadersError = true;
395 break;
396 }
397 const CDir &next = d._subItems[i];
398 if (!subItem.AreMultiPartEqualWith(next))
399 break;
400 i++;
401 ref.NumExtents++;
402 ref.TotalSize += next.Size;
403 if (!next.IsNonFinalExtent())
404 break;
405 }
406 }
407 Refs.Add(ref);
408 CreateRefs(subItem);
409 }
410 }
411
ReadBootInfo()412 void CInArchive::ReadBootInfo()
413 {
414 if (!_bootIsDefined)
415 return;
416 HeadersError = true;
417
418 if (memcmp(_bootDesc.BootSystemId, kElToritoSpec, sizeof(_bootDesc.BootSystemId)) != 0)
419 return;
420
421 UInt32 blockIndex = GetUi32(_bootDesc.BootSystemUse);
422 SeekToBlock(blockIndex);
423
424 Byte buf[32];
425 ReadBytes(buf, 32);
426
427 if (buf[0] != NBootEntryId::kValidationEntry
428 || buf[2] != 0
429 || buf[3] != 0
430 || buf[30] != 0x55
431 || buf[31] != 0xAA)
432 return;
433
434 {
435 UInt32 sum = 0;
436 for (unsigned i = 0; i < 32; i += 2)
437 sum += GetUi16(buf + i);
438 if ((sum & 0xFFFF) != 0)
439 return;
440 /*
441 CBootValidationEntry e;
442 e.PlatformId = buf[1];
443 memcpy(e.Id, buf + 4, sizeof(e.Id));
444 // UInt16 checkSum = GetUi16(p + 28);
445 */
446 }
447
448 ReadBytes(buf, 32);
449 {
450 CBootInitialEntry e;
451 if (!e.Parse(buf))
452 return;
453 BootEntries.Add(e);
454 }
455
456 bool error = false;
457
458 for (;;)
459 {
460 ReadBytes(buf, 32);
461 Byte headerIndicator = buf[0];
462 if (headerIndicator != NBootEntryId::kMoreHeaders
463 && headerIndicator != NBootEntryId::kFinalHeader)
464 break;
465
466 // Section Header
467 // Byte platform = p[1];
468 unsigned numEntries = GetUi16(buf + 2);
469 // id[28]
470
471 for (unsigned i = 0; i < numEntries; i++)
472 {
473 ReadBytes(buf, 32);
474 CBootInitialEntry e;
475 if (!e.Parse(buf))
476 {
477 error = true;
478 break;
479 }
480 if (e.BootMediaType & (1 << 5))
481 {
482 // Section entry extension
483 for (unsigned j = 0;; j++)
484 {
485 ReadBytes(buf, 32);
486 if (j > 32 || buf[0] != NBootEntryId::kExtensionIndicator)
487 {
488 error = true;
489 break;
490 }
491 if ((buf[1] & (1 << 5)) == 0)
492 break;
493 // info += (buf + 2, 30)
494 }
495 }
496 BootEntries.Add(e);
497 }
498
499 if (headerIndicator != NBootEntryId::kMoreHeaders)
500 break;
501 }
502
503 HeadersError = error;
504 }
505
Open2()506 HRESULT CInArchive::Open2()
507 {
508 _position = 0;
509 RINOK(_stream->Seek(0, STREAM_SEEK_END, &_fileSize));
510 if (_fileSize < kStartPos)
511 return S_FALSE;
512 RINOK(_stream->Seek(kStartPos, STREAM_SEEK_SET, &_position));
513
514 PhySize = _position;
515 m_BufferPos = 0;
516 // BlockSize = kBlockSize;
517
518 for (;;)
519 {
520 Byte sig[7];
521 ReadBytes(sig, 7);
522 Byte ver = sig[6];
523
524 if (!CheckSignature(kSig_CD001, sig + 1))
525 {
526 return S_FALSE;
527 /*
528 if (sig[0] != 0 || ver != 1)
529 break;
530 if (CheckSignature(kSig_BEA01, sig + 1))
531 {
532 }
533 else if (CheckSignature(kSig_TEA01, sig + 1))
534 {
535 break;
536 }
537 else if (CheckSignature(kSig_NSR02, sig + 1))
538 {
539 }
540 else
541 break;
542 SkipZeros(0x800 - 7);
543 continue;
544 */
545 }
546
547 // version = 2 for ISO 9660:1999?
548 if (ver > 2)
549 return S_FALSE;
550
551 if (sig[0] == NVolDescType::kTerminator)
552 {
553 break;
554 // Skip(0x800 - 7);
555 // continue;
556 }
557
558 switch (sig[0])
559 {
560 case NVolDescType::kBootRecord:
561 {
562 _bootIsDefined = true;
563 ReadBootRecordDescriptor(_bootDesc);
564 break;
565 }
566 case NVolDescType::kPrimaryVol:
567 case NVolDescType::kSupplementaryVol:
568 {
569 // some ISOs have two PrimaryVols.
570 CVolumeDescriptor vd;
571 ReadVolumeDescriptor(vd);
572 if (sig[0] == NVolDescType::kPrimaryVol)
573 {
574 // some burners write "Joliet" Escape Sequence to primary volume
575 memset(vd.EscapeSequence, 0, sizeof(vd.EscapeSequence));
576 }
577 VolDescs.Add(vd);
578 break;
579 }
580 default:
581 break;
582 }
583 }
584
585 if (VolDescs.IsEmpty())
586 return S_FALSE;
587 for (MainVolDescIndex = VolDescs.Size() - 1; MainVolDescIndex > 0; MainVolDescIndex--)
588 if (VolDescs[MainVolDescIndex].IsJoliet())
589 break;
590 // MainVolDescIndex = 0; // to read primary volume
591 const CVolumeDescriptor &vd = VolDescs[MainVolDescIndex];
592 if (vd.LogicalBlockSize != kBlockSize)
593 return S_FALSE;
594
595 IsArc = true;
596
597 (CDirRecord &)_rootDir = vd.RootDirRecord;
598 ReadDir(_rootDir, 0);
599 CreateRefs(_rootDir);
600 ReadBootInfo();
601
602 {
603 FOR_VECTOR (i, Refs)
604 {
605 const CRef &ref = Refs[i];
606 for (UInt32 j = 0; j < ref.NumExtents; j++)
607 {
608 const CDir &item = ref.Dir->_subItems[ref.Index + j];
609 if (!item.IsDir() && item.Size != 0)
610 UpdatePhySize(item.ExtentLocation, item.Size);
611 }
612 }
613 }
614 {
615 FOR_VECTOR (i, BootEntries)
616 {
617 const CBootInitialEntry &be = BootEntries[i];
618 UpdatePhySize(be.LoadRBA, GetBootItemSize(i));
619 }
620 }
621
622 if (PhySize < _fileSize)
623 {
624 UInt64 rem = _fileSize - PhySize;
625 const UInt64 kRemMax = 1 << 21;
626 if (rem <= kRemMax)
627 {
628 RINOK(_stream->Seek(PhySize, STREAM_SEEK_SET, NULL));
629 bool areThereNonZeros = false;
630 UInt64 numZeros = 0;
631 RINOK(ReadZeroTail(_stream, areThereNonZeros, numZeros, kRemMax));
632 if (!areThereNonZeros)
633 PhySize += numZeros;
634 }
635 }
636
637 return S_OK;
638 }
639
Open(IInStream * inStream)640 HRESULT CInArchive::Open(IInStream *inStream)
641 {
642 Clear();
643 _stream = inStream;
644 try { return Open2(); }
645 catch(const CSystemException &e) { return e.ErrorCode; }
646 catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; }
647 catch(CHeaderErrorException &) { HeadersError = true; return S_FALSE; }
648 catch(CEndianErrorException &) { IncorrectBigEndian = true; return S_FALSE; }
649 }
650
Clear()651 void CInArchive::Clear()
652 {
653 IsArc = false;
654 UnexpectedEnd = false;
655 HeadersError = false;
656 IncorrectBigEndian = false;
657 TooDeepDirs = false;
658 SelfLinkedDirs = false;
659
660 UniqStartLocations.Clear();
661
662 Refs.Clear();
663 _rootDir.Clear();
664 VolDescs.Clear();
665 _bootIsDefined = false;
666 BootEntries.Clear();
667 SuspSkipSize = 0;
668 IsSusp = false;
669 }
670
671 }}
672