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