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