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