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