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 static const Byte kSig_NSR02[5] = { 'N', 'S', 'R', '0', '2' };
303 static const Byte kSig_NSR03[5] = { 'N', 'S', 'R', '0', '3' };
304 static const Byte kSig_BEA01[5] = { 'B', 'E', 'A', '0', '1' };
305 static const Byte kSig_TEA01[5] = { 'T', 'E', 'A', '0', '1' };
306
CheckSignature(const Byte * sig,const Byte * data)307 static inline bool CheckSignature(const Byte *sig, const Byte *data)
308 {
309 for (int i = 0; i < 5; i++)
310 if (sig[i] != data[i])
311 return false;
312 return true;
313 }
314
SeekToBlock(UInt32 blockIndex)315 void CInArchive::SeekToBlock(UInt32 blockIndex)
316 {
317 HRESULT res = _stream->Seek((UInt64)blockIndex * VolDescs[MainVolDescIndex].LogicalBlockSize, STREAM_SEEK_SET, &_position);
318 if (res != S_OK)
319 throw CSystemException(res);
320 m_BufferPos = 0;
321 }
322
323 static const int kNumLevelsMax = 256;
324
ReadDir(CDir & d,int level)325 void CInArchive::ReadDir(CDir &d, int level)
326 {
327 if (!d.IsDir())
328 return;
329 if (level > kNumLevelsMax)
330 {
331 TooDeepDirs = true;
332 return;
333 }
334
335 {
336 FOR_VECTOR (i, UniqStartLocations)
337 if (UniqStartLocations[i] == d.ExtentLocation)
338 {
339 SelfLinkedDirs = true;
340 return;
341 }
342 UniqStartLocations.Add(d.ExtentLocation);
343 }
344
345 SeekToBlock(d.ExtentLocation);
346 UInt64 startPos = _position;
347
348 bool firstItem = true;
349 for (;;)
350 {
351 UInt64 offset = _position - startPos;
352 if (offset >= d.Size)
353 break;
354 Byte len = ReadByte();
355 if (len == 0)
356 continue;
357 CDir subItem;
358 ReadDirRecord2(subItem, len);
359 if (firstItem && level == 0)
360 IsSusp = subItem.CheckSusp(SuspSkipSize);
361
362 if (!subItem.IsSystemItem())
363 d._subItems.Add(subItem);
364
365 firstItem = false;
366 }
367 FOR_VECTOR (i, d._subItems)
368 ReadDir(d._subItems[i], level + 1);
369
370 UniqStartLocations.DeleteBack();
371 }
372
CreateRefs(CDir & d)373 void CInArchive::CreateRefs(CDir &d)
374 {
375 if (!d.IsDir())
376 return;
377 for (unsigned i = 0; i < d._subItems.Size();)
378 {
379 CRef ref;
380 CDir &subItem = d._subItems[i];
381 subItem.Parent = &d;
382 ref.Dir = &d;
383 ref.Index = i++;
384 ref.NumExtents = 1;
385 ref.TotalSize = subItem.Size;
386 if (subItem.IsNonFinalExtent())
387 {
388 for (;;)
389 {
390 if (i == d._subItems.Size())
391 {
392 HeadersError = true;
393 break;
394 }
395 const CDir &next = d._subItems[i];
396 if (!subItem.AreMultiPartEqualWith(next))
397 break;
398 i++;
399 ref.NumExtents++;
400 ref.TotalSize += next.Size;
401 if (!next.IsNonFinalExtent())
402 break;
403 }
404 }
405 Refs.Add(ref);
406 CreateRefs(subItem);
407 }
408 }
409
ReadBootInfo()410 void CInArchive::ReadBootInfo()
411 {
412 if (!_bootIsDefined)
413 return;
414 HeadersError = true;
415
416 if (memcmp(_bootDesc.BootSystemId, kElToritoSpec, sizeof(_bootDesc.BootSystemId)) != 0)
417 return;
418
419 UInt32 blockIndex = GetUi32(_bootDesc.BootSystemUse);
420 SeekToBlock(blockIndex);
421
422 Byte buf[32];
423 ReadBytes(buf, 32);
424
425 if (buf[0] != NBootEntryId::kValidationEntry
426 || buf[2] != 0
427 || buf[3] != 0
428 || buf[30] != 0x55
429 || buf[31] != 0xAA)
430 return;
431
432 {
433 UInt32 sum = 0;
434 for (unsigned i = 0; i < 32; i += 2)
435 sum += GetUi16(buf + i);
436 if ((sum & 0xFFFF) != 0)
437 return;
438 /*
439 CBootValidationEntry e;
440 e.PlatformId = buf[1];
441 memcpy(e.Id, buf + 4, sizeof(e.Id));
442 // UInt16 checkSum = GetUi16(p + 28);
443 */
444 }
445
446 ReadBytes(buf, 32);
447 {
448 CBootInitialEntry e;
449 if (!e.Parse(buf))
450 return;
451 BootEntries.Add(e);
452 }
453
454 bool error = false;
455
456 for (;;)
457 {
458 ReadBytes(buf, 32);
459 Byte headerIndicator = buf[0];
460 if (headerIndicator != NBootEntryId::kMoreHeaders
461 && headerIndicator != NBootEntryId::kFinalHeader)
462 break;
463
464 // Section Header
465 // Byte platform = p[1];
466 unsigned numEntries = GetUi16(buf + 2);
467 // id[28]
468
469 for (unsigned i = 0; i < numEntries; i++)
470 {
471 ReadBytes(buf, 32);
472 CBootInitialEntry e;
473 if (!e.Parse(buf))
474 {
475 error = true;
476 break;
477 }
478 if (e.BootMediaType & (1 << 5))
479 {
480 // Section entry extension
481 for (unsigned j = 0;; j++)
482 {
483 ReadBytes(buf, 32);
484 if (j > 32 || buf[0] != NBootEntryId::kExtensionIndicator)
485 {
486 error = true;
487 break;
488 }
489 if ((buf[1] & (1 << 5)) == 0)
490 break;
491 // info += (buf + 2, 30)
492 }
493 }
494 BootEntries.Add(e);
495 }
496
497 if (headerIndicator != NBootEntryId::kMoreHeaders)
498 break;
499 }
500
501 HeadersError = error;
502 }
503
Open2()504 HRESULT CInArchive::Open2()
505 {
506 _position = 0;
507 RINOK(_stream->Seek(0, STREAM_SEEK_END, &_fileSize));
508 if (_fileSize < kStartPos)
509 return S_FALSE;
510 RINOK(_stream->Seek(kStartPos, STREAM_SEEK_SET, &_position));
511
512 PhySize = _position;
513 m_BufferPos = 0;
514 // BlockSize = kBlockSize;
515
516 for (;;)
517 {
518 Byte sig[7];
519 ReadBytes(sig, 7);
520 Byte ver = sig[6];
521
522 if (!CheckSignature(kSig_CD001, sig + 1))
523 {
524 return S_FALSE;
525 /*
526 if (sig[0] != 0 || ver != 1)
527 break;
528 if (CheckSignature(kSig_BEA01, sig + 1))
529 {
530 }
531 else if (CheckSignature(kSig_TEA01, sig + 1))
532 {
533 break;
534 }
535 else if (CheckSignature(kSig_NSR02, sig + 1))
536 {
537 }
538 else
539 break;
540 SkipZeros(0x800 - 7);
541 continue;
542 */
543 }
544
545 // version = 2 for ISO 9660:1999?
546 if (ver > 2)
547 return S_FALSE;
548
549 if (sig[0] == NVolDescType::kTerminator)
550 {
551 break;
552 // Skip(0x800 - 7);
553 // continue;
554 }
555
556 switch (sig[0])
557 {
558 case NVolDescType::kBootRecord:
559 {
560 _bootIsDefined = true;
561 ReadBootRecordDescriptor(_bootDesc);
562 break;
563 }
564 case NVolDescType::kPrimaryVol:
565 case NVolDescType::kSupplementaryVol:
566 {
567 // some ISOs have two PrimaryVols.
568 CVolumeDescriptor vd;
569 ReadVolumeDescriptor(vd);
570 if (sig[0] == NVolDescType::kPrimaryVol)
571 {
572 // some burners write "Joliet" Escape Sequence to primary volume
573 memset(vd.EscapeSequence, 0, sizeof(vd.EscapeSequence));
574 }
575 VolDescs.Add(vd);
576 break;
577 }
578 default:
579 break;
580 }
581 }
582
583 if (VolDescs.IsEmpty())
584 return S_FALSE;
585 for (MainVolDescIndex = VolDescs.Size() - 1; MainVolDescIndex > 0; MainVolDescIndex--)
586 if (VolDescs[MainVolDescIndex].IsJoliet())
587 break;
588 // MainVolDescIndex = 0; // to read primary volume
589 const CVolumeDescriptor &vd = VolDescs[MainVolDescIndex];
590 if (vd.LogicalBlockSize != kBlockSize)
591 return S_FALSE;
592
593 IsArc = true;
594
595 (CDirRecord &)_rootDir = vd.RootDirRecord;
596 ReadDir(_rootDir, 0);
597 CreateRefs(_rootDir);
598 ReadBootInfo();
599
600 {
601 FOR_VECTOR (i, Refs)
602 {
603 const CRef &ref = Refs[i];
604 for (UInt32 j = 0; j < ref.NumExtents; j++)
605 {
606 const CDir &item = ref.Dir->_subItems[ref.Index + j];
607 if (!item.IsDir() && item.Size != 0)
608 UpdatePhySize(item.ExtentLocation, item.Size);
609 }
610 }
611 }
612 {
613 FOR_VECTOR (i, BootEntries)
614 {
615 const CBootInitialEntry &be = BootEntries[i];
616 UpdatePhySize(be.LoadRBA, GetBootItemSize(i));
617 }
618 }
619
620 if (PhySize < _fileSize)
621 {
622 UInt64 rem = _fileSize - PhySize;
623 const UInt64 kRemMax = 1 << 21;
624 if (rem <= kRemMax)
625 {
626 RINOK(_stream->Seek(PhySize, STREAM_SEEK_SET, NULL));
627 bool areThereNonZeros = false;
628 UInt64 numZeros = 0;
629 RINOK(ReadZeroTail(_stream, areThereNonZeros, numZeros, kRemMax));
630 if (!areThereNonZeros)
631 PhySize += numZeros;
632 }
633 }
634
635 return S_OK;
636 }
637
Open(IInStream * inStream)638 HRESULT CInArchive::Open(IInStream *inStream)
639 {
640 Clear();
641 _stream = inStream;
642 try { return Open2(); }
643 catch(const CSystemException &e) { return e.ErrorCode; }
644 catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; }
645 catch(CHeaderErrorException &) { HeadersError = true; return S_FALSE; }
646 catch(CEndianErrorException &) { IncorrectBigEndian = true; return S_FALSE; }
647 }
648
Clear()649 void CInArchive::Clear()
650 {
651 IsArc = false;
652 UnexpectedEnd = false;
653 HeadersError = false;
654 IncorrectBigEndian = false;
655 TooDeepDirs = false;
656 SelfLinkedDirs = false;
657
658 UniqStartLocations.Clear();
659
660 Refs.Clear();
661 _rootDir.Clear();
662 VolDescs.Clear();
663 _bootIsDefined = false;
664 BootEntries.Clear();
665 SuspSkipSize = 0;
666 IsSusp = false;
667 }
668
669 }}
670