1 // IsoHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/MyLinux.h"
7 #include "../../../Common/StringConvert.h"
8 
9 #include "../../../Windows/PropVariant.h"
10 #include "../../../Windows/TimeUtils.h"
11 
12 #include "../../Common/LimitedStreams.h"
13 #include "../../Common/ProgressUtils.h"
14 
15 #include "../../Compress/CopyCoder.h"
16 
17 #include "../Common/ItemNameUtils.h"
18 
19 #include "IsoHandler.h"
20 
21 using namespace NWindows;
22 using namespace NTime;
23 
24 namespace NArchive {
25 namespace NIso {
26 
27 static const Byte kProps[] =
28 {
29   kpidPath,
30   kpidIsDir,
31   kpidSize,
32   kpidPackSize,
33   kpidMTime,
34   // kpidCTime,
35   // kpidATime,
36   kpidPosixAttrib,
37   // kpidUser,
38   // kpidGroup,
39   // kpidLinks,
40   kpidSymLink
41 };
42 
43 static const Byte kArcProps[] =
44 {
45   kpidComment,
46   kpidCTime,
47   kpidMTime,
48   // kpidHeadersSize
49 };
50 
51 IMP_IInArchive_Props
52 IMP_IInArchive_ArcProps
53 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)54 STDMETHODIMP CHandler::Open(IInStream *stream,
55     const UInt64 * /* maxCheckStartPosition */,
56     IArchiveOpenCallback * /* openArchiveCallback */)
57 {
58   COM_TRY_BEGIN
59   Close();
60   {
61     RINOK(_archive.Open(stream));
62     _stream = stream;
63   }
64   return S_OK;
65   COM_TRY_END
66 }
67 
Close()68 STDMETHODIMP CHandler::Close()
69 {
70   _archive.Clear();
71   _stream.Release();
72   return S_OK;
73 }
74 
GetNumberOfItems(UInt32 * numItems)75 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
76 {
77   *numItems = _archive.Refs.Size() + _archive.BootEntries.Size();
78   return S_OK;
79 }
80 
AddString(AString & s,const char * name,const Byte * p,unsigned size)81 static void AddString(AString &s, const char *name, const Byte *p, unsigned size)
82 {
83   unsigned i;
84   for (i = 0; i < size && p[i]; i++);
85   for (; i > 0 && p[i - 1] == ' '; i--);
86   if (i != 0)
87   {
88     AString d;
89     d.SetFrom((const char *)p, i);
90     s += '\n';
91     s += name;
92     s += ": ";
93     s += d;
94   }
95 }
96 
97 #define ADD_STRING(n, v) AddString(s, n, vol. v, sizeof(vol. v))
98 
AddErrorMessage(AString & s,const char * message)99 static void AddErrorMessage(AString &s, const char *message)
100 {
101   if (!s.IsEmpty())
102     s += ". ";
103   s += message;
104 }
105 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)106 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
107 {
108   COM_TRY_BEGIN
109   NCOM::CPropVariant prop;
110   if (_stream)
111   {
112   const CVolumeDescriptor &vol = _archive.VolDescs[_archive.MainVolDescIndex];
113   switch (propID)
114   {
115     case kpidComment:
116     {
117       AString s;
118       ADD_STRING("System", SystemId);
119       ADD_STRING("Volume", VolumeId);
120       ADD_STRING("VolumeSet", VolumeSetId);
121       ADD_STRING("Publisher", PublisherId);
122       ADD_STRING("Preparer", DataPreparerId);
123       ADD_STRING("Application", ApplicationId);
124       ADD_STRING("Copyright", CopyrightFileId);
125       ADD_STRING("Abstract", AbstractFileId);
126       ADD_STRING("Bib", BibFileId);
127       prop = s;
128       break;
129     }
130     case kpidCTime: { FILETIME utc; if (vol.CTime.GetFileTime(utc)) prop = utc; break; }
131     case kpidMTime: { FILETIME utc; if (vol.MTime.GetFileTime(utc)) prop = utc; break; }
132   }
133   }
134 
135   switch (propID)
136   {
137     case kpidPhySize: prop = _archive.PhySize; break;
138     case kpidErrorFlags:
139     {
140       UInt32 v = 0;
141       if (!_archive.IsArc) v |= kpv_ErrorFlags_IsNotArc;
142       if (_archive.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
143       if (_archive.HeadersError) v |= kpv_ErrorFlags_HeadersError;
144       prop = v;
145       break;
146     }
147 
148     case kpidError:
149     {
150       AString s;
151       if (_archive.IncorrectBigEndian)
152         AddErrorMessage(s, "Incorrect big-endian headers");
153       if (_archive.SelfLinkedDirs)
154         AddErrorMessage(s, "Self-linked directory");
155       if (_archive.TooDeepDirs)
156         AddErrorMessage(s, "Too deep directory levels");
157       if (!s.IsEmpty())
158         prop = s;
159       break;
160     }
161   }
162   prop.Detach(value);
163   return S_OK;
164   COM_TRY_END
165 }
166 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)167 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
168 {
169   COM_TRY_BEGIN
170   NCOM::CPropVariant prop;
171   if (index >= (UInt32)_archive.Refs.Size())
172   {
173     index -= _archive.Refs.Size();
174     const CBootInitialEntry &be = _archive.BootEntries[index];
175     switch (propID)
176     {
177       case kpidPath:
178       {
179         AString s ("[BOOT]" STRING_PATH_SEPARATOR);
180         if (_archive.BootEntries.Size() != 1)
181         {
182           s.Add_UInt32(index + 1);
183           s += '-';
184         }
185         s += be.GetName();
186         prop = s;
187         break;
188       }
189       case kpidIsDir: prop = false; break;
190       case kpidSize:
191       case kpidPackSize:
192         prop = (UInt64)_archive.GetBootItemSize(index);
193         break;
194     }
195   }
196   else
197   {
198     const CRef &ref = _archive.Refs[index];
199     const CDir &item = ref.Dir->_subItems[ref.Index];
200     switch (propID)
201     {
202       case kpidPath:
203         // if (item.FileId.GetCapacity() >= 0)
204         {
205           UString s;
206           if (_archive.IsJoliet())
207             item.GetPathU(s);
208           else
209             s = MultiByteToUnicodeString(item.GetPath(_archive.IsSusp, _archive.SuspSkipSize), CP_OEMCP);
210 
211           if (s.Len() >= 2 && s[s.Len() - 2] == ';' && s.Back() == '1')
212             s.DeleteFrom(s.Len() - 2);
213 
214           if (!s.IsEmpty() && s.Back() == L'.')
215             s.DeleteBack();
216 
217           NItemName::ReplaceToOsSlashes_Remove_TailSlash(s);
218           prop = s;
219         }
220         break;
221 
222       case kpidSymLink:
223         if (_archive.IsSusp)
224         {
225           UInt32 mode;
226           if (item.GetPx(_archive.SuspSkipSize, k_Px_Mode, mode))
227           {
228             if (MY_LIN_S_ISLNK(mode))
229             {
230               AString s8;
231               if (item.GetSymLink(_archive.SuspSkipSize, s8))
232               {
233                 UString 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