1 // IsoHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringConvert.h"
7 
8 #include "../../../Windows/PropVariant.h"
9 #include "../../../Windows/TimeUtils.h"
10 
11 #include "../../Common/LimitedStreams.h"
12 #include "../../Common/ProgressUtils.h"
13 
14 #include "../../Compress/CopyCoder.h"
15 
16 #include "../Common/ItemNameUtils.h"
17 
18 #include "IsoHandler.h"
19 
20 using namespace NWindows;
21 using namespace NTime;
22 
23 namespace NArchive {
24 namespace NIso {
25 
26 static const Byte kProps[] =
27 {
28   kpidPath,
29   kpidIsDir,
30   kpidSize,
31   kpidPackSize,
32   kpidMTime,
33   // kpidCTime,
34   // kpidATime,
35   kpidPosixAttrib,
36   // kpidUser,
37   // kpidGroup,
38   // kpidLinks,
39   kpidSymLink
40 };
41 
42 static const Byte kArcProps[] =
43 {
44   kpidComment,
45   kpidCTime,
46   kpidMTime,
47   // kpidHeadersSize
48 };
49 
50 IMP_IInArchive_Props
51 IMP_IInArchive_ArcProps
52 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)53 STDMETHODIMP CHandler::Open(IInStream *stream,
54     const UInt64 * /* maxCheckStartPosition */,
55     IArchiveOpenCallback * /* openArchiveCallback */)
56 {
57   COM_TRY_BEGIN
58   Close();
59   {
60     RINOK(_archive.Open(stream));
61     _stream = stream;
62   }
63   return S_OK;
64   COM_TRY_END
65 }
66 
Close()67 STDMETHODIMP CHandler::Close()
68 {
69   _archive.Clear();
70   _stream.Release();
71   return S_OK;
72 }
73 
GetNumberOfItems(UInt32 * numItems)74 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
75 {
76   *numItems = _archive.Refs.Size() + _archive.BootEntries.Size();
77   return S_OK;
78 }
79 
AddString(AString & s,const char * name,const Byte * p,unsigned size)80 static void AddString(AString &s, const char *name, const Byte *p, unsigned size)
81 {
82   unsigned i;
83   for (i = 0; i < size && p[i]; i++);
84   for (; i > 0 && p[i - 1] == ' '; i--);
85   if (i != 0)
86   {
87     AString d;
88     d.SetFrom((const char *)p, i);
89     s += '\n';
90     s += name;
91     s += ": ";
92     s += d;
93   }
94 }
95 
96 #define ADD_STRING(n, v) AddString(s, n, vol. v, sizeof(vol. v))
97 
AddErrorMessage(AString & s,const char * message)98 static void AddErrorMessage(AString &s, const char *message)
99 {
100   if (!s.IsEmpty())
101     s += ". ";
102   s += message;
103 }
104 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)105 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
106 {
107   COM_TRY_BEGIN
108   NCOM::CPropVariant prop;
109   if (_stream)
110   {
111   const CVolumeDescriptor &vol = _archive.VolDescs[_archive.MainVolDescIndex];
112   switch (propID)
113   {
114     case kpidComment:
115     {
116       AString s;
117       ADD_STRING("System", SystemId);
118       ADD_STRING("Volume", VolumeId);
119       ADD_STRING("VolumeSet", VolumeSetId);
120       ADD_STRING("Publisher", PublisherId);
121       ADD_STRING("Preparer", DataPreparerId);
122       ADD_STRING("Application", ApplicationId);
123       ADD_STRING("Copyright", CopyrightFileId);
124       ADD_STRING("Abstract", AbstractFileId);
125       ADD_STRING("Bib", BibFileId);
126       prop = s;
127       break;
128     }
129     case kpidCTime: { FILETIME utc; if (vol.CTime.GetFileTime(utc)) prop = utc; break; }
130     case kpidMTime: { FILETIME utc; if (vol.MTime.GetFileTime(utc)) prop = utc; break; }
131   }
132   }
133 
134   switch (propID)
135   {
136     case kpidPhySize: prop = _archive.PhySize; break;
137     case kpidErrorFlags:
138     {
139       UInt32 v = 0;
140       if (!_archive.IsArc) v |= kpv_ErrorFlags_IsNotArc;
141       if (_archive.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
142       if (_archive.HeadersError) v |= kpv_ErrorFlags_HeadersError;
143       prop = v;
144       break;
145     }
146 
147     case kpidError:
148     {
149       AString s;
150       if (_archive.IncorrectBigEndian)
151         AddErrorMessage(s, "Incorrect big-endian headers");
152       if (_archive.SelfLinkedDirs)
153         AddErrorMessage(s, "Self-linked directory");
154       if (_archive.TooDeepDirs)
155         AddErrorMessage(s, "Too deep directory levels");
156       if (!s.IsEmpty())
157         prop = s;
158       break;
159     }
160   }
161   prop.Detach(value);
162   return S_OK;
163   COM_TRY_END
164 }
165 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)166 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
167 {
168   COM_TRY_BEGIN
169   NCOM::CPropVariant prop;
170   if (index >= (UInt32)_archive.Refs.Size())
171   {
172     index -= _archive.Refs.Size();
173     const CBootInitialEntry &be = _archive.BootEntries[index];
174     switch (propID)
175     {
176       case kpidPath:
177       {
178         AString s ("[BOOT]" STRING_PATH_SEPARATOR);
179         if (_archive.BootEntries.Size() != 1)
180         {
181           s.Add_UInt32(index + 1);
182           s += '-';
183         }
184         s += be.GetName();
185         prop = s;
186         break;
187       }
188       case kpidIsDir: prop = false; break;
189       case kpidSize:
190       case kpidPackSize:
191         prop = (UInt64)_archive.GetBootItemSize(index);
192         break;
193     }
194   }
195   else
196   {
197     const CRef &ref = _archive.Refs[index];
198     const CDir &item = ref.Dir->_subItems[ref.Index];
199     switch (propID)
200     {
201       case kpidPath:
202         // if (item.FileId.GetCapacity() >= 0)
203         {
204           UString s;
205           if (_archive.IsJoliet())
206             item.GetPathU(s);
207           else
208             s = MultiByteToUnicodeString(item.GetPath(_archive.IsSusp, _archive.SuspSkipSize), CP_OEMCP);
209 
210           if (s.Len() >= 2 && s[s.Len() - 2] == ';' && s.Back() == '1')
211             s.DeleteFrom(s.Len() - 2);
212 
213           if (!s.IsEmpty() && s.Back() == L'.')
214             s.DeleteBack();
215 
216           NItemName::ReplaceToOsSlashes_Remove_TailSlash(s);
217           prop = s;
218         }
219         break;
220 
221       case kpidSymLink:
222         if (_archive.IsSusp)
223         {
224           UString s;
225           UInt32 mode;
226           if (item.GetPx(_archive.SuspSkipSize, k_Px_Mode, mode))
227           {
228             if (((mode >> 12) & 0xF) == 10)
229             {
230               AString s8;
231               if (item.GetSymLink(_archive.SuspSkipSize, s8))
232               {
233                 s = MultiByteToUnicodeString(s8, CP_OEMCP);
234                 prop = s;
235               }
236             }
237           }
238         }
239         break;
240 
241 
242       case kpidPosixAttrib:
243       /*
244       case kpidLinks:
245       case kpidUser:
246       case kpidGroup:
247       */
248       {
249         if (_archive.IsSusp)
250         {
251           UInt32 t = 0;
252           switch (propID)
253           {
254             case kpidPosixAttrib: t = k_Px_Mode; break;
255             /*
256             case kpidLinks: t = k_Px_Links; break;
257             case kpidUser: t = k_Px_User; break;
258             case kpidGroup: t = k_Px_Group; break;
259             */
260           }
261           UInt32 v;
262           if (item.GetPx(_archive.SuspSkipSize, t, v))
263             prop = v;
264         }
265         break;
266       }
267 
268       case kpidIsDir: prop = item.IsDir(); break;
269       case kpidSize:
270       case kpidPackSize:
271         if (!item.IsDir())
272           prop = (UInt64)ref.TotalSize;
273         break;
274 
275       case kpidMTime:
276       // case kpidCTime:
277       // case kpidATime:
278       {
279         FILETIME utc;
280         if (/* propID == kpidMTime && */ item.DateTime.GetFileTime(utc))
281           prop = utc;
282         /*
283         else
284         {
285           UInt32 t = 0;
286           switch (propID)
287           {
288             case kpidMTime: t = k_Tf_MTime; break;
289             case kpidCTime: t = k_Tf_CTime; break;
290             case kpidATime: t = k_Tf_ATime; break;
291           }
292           CRecordingDateTime dt;
293           if (item.GetTf(_archive.SuspSkipSize, t, dt))
294           {
295             FILETIME utc;
296             if (dt.GetFileTime(utc))
297               prop = utc;
298           }
299         }
300         */
301         break;
302       }
303     }
304   }
305   prop.Detach(value);
306   return S_OK;
307   COM_TRY_END
308 }
309 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)310 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
311     Int32 testMode, IArchiveExtractCallback *extractCallback)
312 {
313   COM_TRY_BEGIN
314   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
315   if (allFilesMode)
316     numItems = _archive.Refs.Size();
317   if (numItems == 0)
318     return S_OK;
319   UInt64 totalSize = 0;
320   UInt32 i;
321   for (i = 0; i < numItems; i++)
322   {
323     UInt32 index = (allFilesMode ? i : indices[i]);
324     if (index < (UInt32)_archive.Refs.Size())
325     {
326       const CRef &ref = _archive.Refs[index];
327       const CDir &item = ref.Dir->_subItems[ref.Index];
328       if (!item.IsDir())
329         totalSize += ref.TotalSize;
330     }
331     else
332       totalSize += _archive.GetBootItemSize(index - _archive.Refs.Size());
333   }
334   extractCallback->SetTotal(totalSize);
335 
336   UInt64 currentTotalSize = 0;
337   UInt64 currentItemSize;
338 
339   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
340   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
341 
342   CLocalProgress *lps = new CLocalProgress;
343   CMyComPtr<ICompressProgressInfo> progress = lps;
344   lps->Init(extractCallback, false);
345 
346   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
347   CMyComPtr<ISequentialInStream> inStream(streamSpec);
348   streamSpec->SetStream(_stream);
349 
350   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
351   {
352     lps->InSize = lps->OutSize = currentTotalSize;
353     RINOK(lps->SetCur());
354     currentItemSize = 0;
355     CMyComPtr<ISequentialOutStream> realOutStream;
356     Int32 askMode = testMode ?
357         NExtract::NAskMode::kTest :
358         NExtract::NAskMode::kExtract;
359     UInt32 index = allFilesMode ? i : indices[i];
360 
361     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
362 
363     UInt64 blockIndex;
364     if (index < (UInt32)_archive.Refs.Size())
365     {
366       const CRef &ref = _archive.Refs[index];
367       const CDir &item = ref.Dir->_subItems[ref.Index];
368       if (item.IsDir())
369       {
370         RINOK(extractCallback->PrepareOperation(askMode));
371         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
372         continue;
373       }
374       currentItemSize = ref.TotalSize;
375       blockIndex = item.ExtentLocation;
376     }
377     else
378     {
379       unsigned bootIndex = index - _archive.Refs.Size();
380       const CBootInitialEntry &be = _archive.BootEntries[bootIndex];
381       currentItemSize = _archive.GetBootItemSize(bootIndex);
382       blockIndex = be.LoadRBA;
383     }
384 
385 
386     if (!testMode && !realOutStream)
387       continue;
388 
389     RINOK(extractCallback->PrepareOperation(askMode));
390 
391     bool isOK = true;
392     if (index < (UInt32)_archive.Refs.Size())
393     {
394       const CRef &ref = _archive.Refs[index];
395       UInt64 offset = 0;
396       for (UInt32 e = 0; e < ref.NumExtents; e++)
397       {
398         const CDir &item2 = ref.Dir->_subItems[ref.Index + e];
399         if (item2.Size == 0)
400           continue;
401         lps->InSize = lps->OutSize = currentTotalSize + offset;
402         RINOK(_stream->Seek((UInt64)item2.ExtentLocation * kBlockSize, STREAM_SEEK_SET, NULL));
403         streamSpec->Init(item2.Size);
404         RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
405         if (copyCoderSpec->TotalSize != item2.Size)
406         {
407           isOK = false;
408           break;
409         }
410         offset += item2.Size;
411       }
412     }
413     else
414     {
415       RINOK(_stream->Seek((UInt64)blockIndex * kBlockSize, STREAM_SEEK_SET, NULL));
416       streamSpec->Init(currentItemSize);
417       RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
418       if (copyCoderSpec->TotalSize != currentItemSize)
419         isOK = false;
420     }
421     realOutStream.Release();
422     RINOK(extractCallback->SetOperationResult(isOK ?
423         NExtract::NOperationResult::kOK:
424         NExtract::NOperationResult::kDataError));
425   }
426   return S_OK;
427   COM_TRY_END
428 }
429 
GetStream(UInt32 index,ISequentialInStream ** stream)430 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
431 {
432   COM_TRY_BEGIN
433   *stream = 0;
434   UInt64 blockIndex;
435   UInt64 currentItemSize;
436 
437   if (index < _archive.Refs.Size())
438   {
439     const CRef &ref = _archive.Refs[index];
440     const CDir &item = ref.Dir->_subItems[ref.Index];
441     if (item.IsDir())
442       return S_FALSE;
443 
444     if (ref.NumExtents > 1)
445     {
446       CExtentsStream *extentStreamSpec = new CExtentsStream();
447       CMyComPtr<ISequentialInStream> extentStream = extentStreamSpec;
448 
449       extentStreamSpec->Stream = _stream;
450 
451       UInt64 virtOffset = 0;
452       for (UInt32 i = 0; i < ref.NumExtents; i++)
453       {
454         const CDir &item2 = ref.Dir->_subItems[ref.Index + i];
455         if (item2.Size == 0)
456           continue;
457         CSeekExtent se;
458         se.Phy = (UInt64)item2.ExtentLocation * kBlockSize;
459         se.Virt = virtOffset;
460         extentStreamSpec->Extents.Add(se);
461         virtOffset += item2.Size;
462       }
463       if (virtOffset != ref.TotalSize)
464         return S_FALSE;
465       CSeekExtent se;
466       se.Phy = 0;
467       se.Virt = virtOffset;
468       extentStreamSpec->Extents.Add(se);
469       extentStreamSpec->Init();
470       *stream = extentStream.Detach();
471       return S_OK;
472     }
473 
474     currentItemSize = item.Size;
475     blockIndex = item.ExtentLocation;
476   }
477   else
478   {
479     unsigned bootIndex = index - _archive.Refs.Size();
480     const CBootInitialEntry &be = _archive.BootEntries[bootIndex];
481     currentItemSize = _archive.GetBootItemSize(bootIndex);
482     blockIndex = be.LoadRBA;
483   }
484 
485   return CreateLimitedInStream(_stream, (UInt64)blockIndex * kBlockSize, currentItemSize, stream);
486   COM_TRY_END
487 }
488 
489 }}
490