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.Add_Minus();
52
53 if (BootMediaType < Z7_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.Add_Minus();
69 for (i = 1; i < sizeof(VendorSpec); i++)
70 {
71 char c = (char)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 |= ((UInt32)(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 const Byte fileStructureVersion = ReadByte();
291 // d.FileStructureVersion = fileStructureVersion;
292 if (fileStructureVersion != 1 && // ECMA-119
293 fileStructureVersion != 2) // some ISO files have fileStructureVersion == 2.
294 {
295 // v24.05: we ignore that field, because we don't know what exact values are allowed there
296 // throw CHeaderErrorException();
297 }
298 SkipZeros(1); // (Reserved for future standardization)
299 ReadBytes(d.ApplicationUse, sizeof(d.ApplicationUse));
300 // Most ISO contain zeros in the following field (reserved for future standardization).
301 // But some ISO programs write some data to that area.
302 // So we disable check for zeros.
303 Skip(653); // SkipZeros(653);
304 }
305
306 static const Byte kSig_CD001[5] = { 'C', 'D', '0', '0', '1' };
307
308 /*
309 static const Byte kSig_NSR02[5] = { 'N', 'S', 'R', '0', '2' };
310 static const Byte kSig_NSR03[5] = { 'N', 'S', 'R', '0', '3' };
311 static const Byte kSig_BEA01[5] = { 'B', 'E', 'A', '0', '1' };
312 static const Byte kSig_TEA01[5] = { 'T', 'E', 'A', '0', '1' };
313 */
314
CheckSignature(const Byte * sig,const Byte * data)315 static inline bool CheckSignature(const Byte *sig, const Byte *data)
316 {
317 for (int i = 0; i < 5; i++)
318 if (sig[i] != data[i])
319 return false;
320 return true;
321 }
322
SeekToBlock(UInt32 blockIndex)323 void CInArchive::SeekToBlock(UInt32 blockIndex)
324 {
325 const HRESULT res = _stream->Seek(
326 (Int64)((UInt64)blockIndex * VolDescs[MainVolDescIndex].LogicalBlockSize),
327 STREAM_SEEK_SET, &_position);
328 if (res != S_OK)
329 throw CSystemException(res);
330 m_BufferPos = 0;
331 }
332
333 static const int kNumLevelsMax = 256;
334
ReadDir(CDir & d,int level)335 void CInArchive::ReadDir(CDir &d, int level)
336 {
337 if (!d.IsDir())
338 return;
339 if (level > kNumLevelsMax)
340 {
341 TooDeepDirs = true;
342 return;
343 }
344
345 {
346 FOR_VECTOR (i, UniqStartLocations)
347 if (UniqStartLocations[i] == d.ExtentLocation)
348 {
349 SelfLinkedDirs = true;
350 return;
351 }
352 UniqStartLocations.Add(d.ExtentLocation);
353 }
354
355 SeekToBlock(d.ExtentLocation);
356 UInt64 startPos = _position;
357
358 bool firstItem = true;
359 for (;;)
360 {
361 UInt64 offset = _position - startPos;
362 if (offset >= d.Size)
363 break;
364 Byte len = ReadByte();
365 if (len == 0)
366 continue;
367 CDir subItem;
368 ReadDirRecord2(subItem, len);
369 if (firstItem && level == 0)
370 IsSusp = subItem.CheckSusp(SuspSkipSize);
371
372 if (!subItem.IsSystemItem())
373 d._subItems.Add(subItem);
374
375 firstItem = false;
376 }
377 FOR_VECTOR (i, d._subItems)
378 ReadDir(d._subItems[i], level + 1);
379
380 UniqStartLocations.DeleteBack();
381 }
382
CreateRefs(CDir & d)383 void CInArchive::CreateRefs(CDir &d)
384 {
385 if (!d.IsDir())
386 return;
387 for (unsigned i = 0; i < d._subItems.Size();)
388 {
389 CRef ref;
390 CDir &subItem = d._subItems[i];
391 subItem.Parent = &d;
392 ref.Dir = &d;
393 ref.Index = i++;
394 ref.NumExtents = 1;
395 ref.TotalSize = subItem.Size;
396 if (subItem.IsNonFinalExtent())
397 {
398 for (;;)
399 {
400 if (i == d._subItems.Size())
401 {
402 HeadersError = true;
403 break;
404 }
405 const CDir &next = d._subItems[i];
406 if (!subItem.AreMultiPartEqualWith(next))
407 break;
408 i++;
409 ref.NumExtents++;
410 ref.TotalSize += next.Size;
411 if (!next.IsNonFinalExtent())
412 break;
413 }
414 }
415 Refs.Add(ref);
416 CreateRefs(subItem);
417 }
418 }
419
ReadBootInfo()420 void CInArchive::ReadBootInfo()
421 {
422 if (!_bootIsDefined)
423 return;
424 HeadersError = true;
425
426 if (memcmp(_bootDesc.BootSystemId, kElToritoSpec, sizeof(_bootDesc.BootSystemId)) != 0)
427 return;
428
429 UInt32 blockIndex = GetUi32(_bootDesc.BootSystemUse);
430 SeekToBlock(blockIndex);
431
432 Byte buf[32];
433 ReadBytes(buf, 32);
434
435 if (buf[0] != NBootEntryId::kValidationEntry
436 || buf[2] != 0
437 || buf[3] != 0
438 || buf[30] != 0x55
439 || buf[31] != 0xAA)
440 return;
441
442 {
443 UInt32 sum = 0;
444 for (unsigned i = 0; i < 32; i += 2)
445 sum += GetUi16(buf + i);
446 if ((sum & 0xFFFF) != 0)
447 return;
448 /*
449 CBootValidationEntry e;
450 e.PlatformId = buf[1];
451 memcpy(e.Id, buf + 4, sizeof(e.Id));
452 // UInt16 checkSum = GetUi16(p + 28);
453 */
454 }
455
456 ReadBytes(buf, 32);
457 {
458 CBootInitialEntry e;
459 if (!e.Parse(buf))
460 return;
461 BootEntries.Add(e);
462 }
463
464 bool error = false;
465
466 for (;;)
467 {
468 ReadBytes(buf, 32);
469 Byte headerIndicator = buf[0];
470 if (headerIndicator != NBootEntryId::kMoreHeaders
471 && headerIndicator != NBootEntryId::kFinalHeader)
472 break;
473
474 // Section Header
475 // Byte platform = p[1];
476 unsigned numEntries = GetUi16(buf + 2);
477 // id[28]
478
479 for (unsigned i = 0; i < numEntries; i++)
480 {
481 ReadBytes(buf, 32);
482 CBootInitialEntry e;
483 if (!e.Parse(buf))
484 {
485 error = true;
486 break;
487 }
488 if (e.BootMediaType & (1 << 5))
489 {
490 // Section entry extension
491 for (unsigned j = 0;; j++)
492 {
493 ReadBytes(buf, 32);
494 if (j > 32 || buf[0] != NBootEntryId::kExtensionIndicator)
495 {
496 error = true;
497 break;
498 }
499 if ((buf[1] & (1 << 5)) == 0)
500 break;
501 // info += (buf + 2, 30)
502 }
503 }
504 BootEntries.Add(e);
505 }
506
507 if (headerIndicator != NBootEntryId::kMoreHeaders)
508 break;
509 }
510
511 HeadersError = error;
512 }
513
Open2()514 HRESULT CInArchive::Open2()
515 {
516 _position = 0;
517 RINOK(InStream_GetSize_SeekToEnd(_stream, _fileSize))
518 if (_fileSize < kStartPos)
519 return S_FALSE;
520 RINOK(_stream->Seek(kStartPos, STREAM_SEEK_SET, &_position))
521
522 PhySize = _position;
523 m_BufferPos = 0;
524 // BlockSize = kBlockSize;
525
526 for (;;)
527 {
528 Byte sig[7];
529 ReadBytes(sig, 7);
530 Byte ver = sig[6];
531
532 if (!CheckSignature(kSig_CD001, sig + 1))
533 {
534 return S_FALSE;
535 /*
536 if (sig[0] != 0 || ver != 1)
537 break;
538 if (CheckSignature(kSig_BEA01, sig + 1))
539 {
540 }
541 else if (CheckSignature(kSig_TEA01, sig + 1))
542 {
543 break;
544 }
545 else if (CheckSignature(kSig_NSR02, sig + 1))
546 {
547 }
548 else
549 break;
550 SkipZeros(0x800 - 7);
551 continue;
552 */
553 }
554
555 // version = 2 for ISO 9660:1999?
556 if (ver > 2)
557 return S_FALSE;
558
559 if (sig[0] == NVolDescType::kTerminator)
560 {
561 break;
562 // Skip(0x800 - 7);
563 // continue;
564 }
565
566 switch (sig[0])
567 {
568 case NVolDescType::kBootRecord:
569 {
570 _bootIsDefined = true;
571 ReadBootRecordDescriptor(_bootDesc);
572 break;
573 }
574 case NVolDescType::kPrimaryVol:
575 case NVolDescType::kSupplementaryVol:
576 {
577 // some ISOs have two PrimaryVols.
578 CVolumeDescriptor vd;
579 ReadVolumeDescriptor(vd);
580 if (sig[0] == NVolDescType::kPrimaryVol)
581 {
582 // some burners write "Joliet" Escape Sequence to primary volume
583 memset(vd.EscapeSequence, 0, sizeof(vd.EscapeSequence));
584 }
585 VolDescs.Add(vd);
586 break;
587 }
588 default:
589 break;
590 }
591 }
592
593 if (VolDescs.IsEmpty())
594 return S_FALSE;
595 for (MainVolDescIndex = (int)VolDescs.Size() - 1; MainVolDescIndex > 0; MainVolDescIndex--)
596 if (VolDescs[MainVolDescIndex].IsJoliet())
597 break;
598 /* FIXME: some volume can contain Rock Ridge, that is better than
599 Joliet volume. So we need some way to detect such case */
600 // MainVolDescIndex = 0; // to read primary volume
601 const CVolumeDescriptor &vd = VolDescs[MainVolDescIndex];
602 if (vd.LogicalBlockSize != kBlockSize)
603 return S_FALSE;
604
605 {
606 FOR_VECTOR (i, VolDescs)
607 {
608 const CVolumeDescriptor &vd2 = VolDescs[i];
609 UpdatePhySize(0, vd2.Get_VolumeSpaceSize_inBytes());
610 }
611 }
612
613
614 IsArc = true;
615
616 (CDirRecord &)_rootDir = vd.RootDirRecord;
617 ReadDir(_rootDir, 0);
618 CreateRefs(_rootDir);
619 ReadBootInfo();
620
621 {
622 FOR_VECTOR (i, Refs)
623 {
624 const CRef &ref = Refs[i];
625 for (UInt32 j = 0; j < ref.NumExtents; j++)
626 {
627 const CDir &item = ref.Dir->_subItems[ref.Index + j];
628 if (!item.IsDir() && item.Size != 0)
629 UpdatePhySize(item.ExtentLocation, item.Size);
630 }
631 }
632 }
633
634 {
635 // find boot item for expand:
636 // UEFI Specification : 13.3.2.1. ISO-9660 and El Torito
637 _expand_BootEntries_index = -1;
638 FOR_VECTOR (i, BootEntries)
639 {
640 const CBootInitialEntry &be = BootEntries[i];
641 if (be.SectorCount <= 1 && be.BootMediaType == NBootMediaType::kNoEmulation)
642 if (_expand_BootEntries_index == -1
643 || be.LoadRBA >= BootEntries[_expand_BootEntries_index].LoadRBA)
644 _expand_BootEntries_index = (int)i;
645 }
646 }
647
648 {
649 FOR_VECTOR (i, BootEntries)
650 {
651 const CBootInitialEntry &be = BootEntries[i];
652 UpdatePhySize(be.LoadRBA, GetBootItemSize(i));
653 }
654 }
655
656 if (PhySize < _fileSize)
657 {
658 UInt64 rem = _fileSize - PhySize;
659 const UInt64 kRemMax = 1 << 21;
660 if (rem <= kRemMax)
661 {
662 RINOK(InStream_SeekSet(_stream, PhySize))
663 bool areThereNonZeros = false;
664 UInt64 numZeros = 0;
665 RINOK(ReadZeroTail(_stream, areThereNonZeros, numZeros, kRemMax))
666 if (!areThereNonZeros)
667 PhySize += numZeros;
668 }
669 }
670
671 return S_OK;
672 }
673
Open(IInStream * inStream)674 HRESULT CInArchive::Open(IInStream *inStream)
675 {
676 Clear();
677 _stream = inStream;
678 try { return Open2(); }
679 catch(const CSystemException &e) { return e.ErrorCode; }
680 catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; }
681 catch(CHeaderErrorException &) { HeadersError = true; return S_FALSE; }
682 catch(CEndianErrorException &) { IncorrectBigEndian = true; return S_FALSE; }
683 }
684
Clear()685 void CInArchive::Clear()
686 {
687 IsArc = false;
688 UnexpectedEnd = false;
689 HeadersError = false;
690 IncorrectBigEndian = false;
691 TooDeepDirs = false;
692 SelfLinkedDirs = false;
693
694 UniqStartLocations.Clear();
695
696 Refs.Clear();
697 _rootDir.Clear();
698 VolDescs.Clear();
699 _bootIsDefined = false;
700 BootEntries.Clear();
701 SuspSkipSize = 0;
702 IsSusp = false;
703
704 _expand_BootEntries_index = -1;
705 }
706
707
GetBootItemSize(unsigned index) const708 UInt64 CInArchive::GetBootItemSize(unsigned index) const
709 {
710 const CBootInitialEntry &be = BootEntries[index];
711 UInt64 size = be.GetSize();
712 if (be.BootMediaType == NBootMediaType::k1d2Floppy) size = 1200 << 10;
713 else if (be.BootMediaType == NBootMediaType::k1d44Floppy) size = 1440 << 10;
714 else if (be.BootMediaType == NBootMediaType::k2d88Floppy) size = 2880 << 10;
715 const UInt64 startPos = (UInt64)be.LoadRBA * kBlockSize;
716 if (startPos < _fileSize)
717 {
718 const UInt64 rem = _fileSize - startPos;
719 /*
720 UEFI modification to ISO specification:
721 because SectorCount is 16-bit, size is limited by (32 MB).
722 UEFI Specification :
723 13.3.2.1. ISO-9660 and El Torito
724 If the value of Sector Count is set to 0 or 1,
725 EFI will assume the system partition consumes the space
726 from the beginning of the "no emulation" image to the end of the CD-ROM.
727 */
728 //
729 if ((int)index == _expand_BootEntries_index || rem < size)
730 size = rem;
731 }
732 return size;
733 }
734
735 }}
736