1 // TarHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/IntToString.h"
7 #include "../../../Common/StringConvert.h"
8 #include "../../../Common/UTFConvert.h"
9
10 #include "../../../Windows/TimeUtils.h"
11
12 #include "../../Common/LimitedStreams.h"
13 #include "../../Common/MethodProps.h"
14 #include "../../Common/ProgressUtils.h"
15 #include "../../Common/StreamObjects.h"
16 #include "../../Common/StreamUtils.h"
17
18 #include "../Common/ItemNameUtils.h"
19
20 #include "TarHandler.h"
21
22 using namespace NWindows;
23
24 namespace NArchive {
25 namespace NTar {
26
27 // 21.02: we use UTF8 code page by default, even if some files show error
28 // before 21.02 : CP_OEMCP;
29 // static const UINT k_DefaultCodePage = CP_UTF8;
30
31
32 static const Byte kProps[] =
33 {
34 kpidPath,
35 kpidIsDir,
36 kpidSize,
37 kpidPackSize,
38 kpidMTime,
39 kpidCTime,
40 kpidATime,
41 kpidPosixAttrib,
42 #if 0
43 kpidAttrib,
44 #endif
45 kpidUser,
46 kpidGroup,
47 kpidUserId,
48 kpidGroupId,
49 kpidSymLink,
50 kpidHardLink,
51 kpidCharacts,
52 kpidComment
53 , kpidDeviceMajor
54 , kpidDeviceMinor
55 // , kpidDevice
56 // , kpidHeadersSize // for debug
57 // , kpidOffset // for debug
58 };
59
60 static const Byte kArcProps[] =
61 {
62 kpidHeadersSize,
63 kpidCodePage,
64 kpidCharacts,
65 kpidComment
66 };
67
68 static const char *k_Characts_Prefix = "PREFIX";
69
70 IMP_IInArchive_Props
71 IMP_IInArchive_ArcProps
72
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))73 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
74 {
75 NCOM::CPropVariant prop;
76 switch (propID)
77 {
78 case kpidPhySize: if (_arc._phySize_Defined) prop = _arc._phySize; break;
79 case kpidHeadersSize: if (_arc._phySize_Defined) prop = _arc._headersSize; break;
80 case kpidErrorFlags:
81 {
82 UInt32 flags = 0;
83 if (!_isArc)
84 flags |= kpv_ErrorFlags_IsNotArc;
85 else switch ((int)_arc._error)
86 {
87 case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break;
88 case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break;
89 // case k_ErrorType_OK: break;
90 // case k_ErrorType_Warning: break;
91 // case k_ErrorType_OK:
92 default: break;
93 }
94 if (flags != 0)
95 prop = flags;
96 break;
97 }
98
99 case kpidWarningFlags:
100 {
101 if (_arc._is_Warning)
102 prop = kpv_ErrorFlags_HeadersError;
103 break;
104 }
105
106 case kpidCodePage:
107 {
108 char sz[16];
109 const char *name = NULL;
110 switch (_openCodePage)
111 {
112 case CP_OEMCP: name = "OEM"; break;
113 case CP_UTF8: name = "UTF-8"; break;
114 default: break;
115 }
116 if (!name)
117 {
118 ConvertUInt32ToString(_openCodePage, sz);
119 name = sz;
120 }
121 prop = name;
122 break;
123 }
124
125 case kpidCharacts:
126 {
127 AString s;
128 if (_arc._are_Gnu) s.Add_OptSpaced("GNU");
129 if (_arc._are_Posix) s.Add_OptSpaced("POSIX");
130 if (_arc._are_Pax_Items) s.Add_OptSpaced("PAX_ITEM");
131 if (_arc._pathPrefix_WasUsed) s.Add_OptSpaced(k_Characts_Prefix);
132 if (_arc._are_LongName) s.Add_OptSpaced("LongName");
133 if (_arc._are_LongLink) s.Add_OptSpaced("LongLink");
134 if (_arc._are_Pax) s.Add_OptSpaced("PAX");
135 if (_arc._are_pax_path) s.Add_OptSpaced("path");
136 if (_arc._are_pax_link) s.Add_OptSpaced("linkpath");
137 if (_arc._are_mtime) s.Add_OptSpaced("mtime");
138 if (_arc._are_atime) s.Add_OptSpaced("atime");
139 if (_arc._are_ctime) s.Add_OptSpaced("ctime");
140 if (_arc._are_SCHILY_fflags) s.Add_OptSpaced("SCHILY.fflags");
141 if (_arc._is_PaxGlobal_Error) s.Add_OptSpaced("PAX_GLOBAL_ERROR");
142 s.Add_OptSpaced(_encodingCharacts.GetCharactsString());
143 prop = s;
144 break;
145 }
146
147 case kpidComment:
148 {
149 if (_arc.PaxGlobal_Defined)
150 {
151 AString s;
152 _arc.PaxGlobal.Print_To_String(s);
153 if (!s.IsEmpty())
154 prop = s;
155 }
156 break;
157 }
158 default: break;
159 }
160 prop.Detach(value);
161 return S_OK;
162 }
163
164
Check(const AString & s)165 void CEncodingCharacts::Check(const AString &s)
166 {
167 IsAscii = s.IsAscii();
168 if (!IsAscii)
169 {
170 /*
171 {
172 Oem_Checked = true;
173 UString u;
174 MultiByteToUnicodeString2(u, s, CP_OEMCP);
175 Oem_Ok = (u.Find((wchar_t)0xfffd) <= 0);
176 }
177 Utf_Checked = true;
178 */
179 UtfCheck.Check_AString(s);
180 }
181 }
182
183
GetCharactsString() const184 AString CEncodingCharacts::GetCharactsString() const
185 {
186 AString s;
187 if (IsAscii)
188 {
189 s += "ASCII";
190 }
191 /*
192 if (Oem_Checked)
193 {
194 s.Add_Space_if_NotEmpty();
195 s += (Oem_Ok ? "oem-ok" : "oem-error");
196 }
197 if (Utf_Checked)
198 */
199 else
200 {
201 s.Add_Space_if_NotEmpty();
202 s += (UtfCheck.IsOK() ? "UTF8" : "UTF8-ERROR"); // "UTF8-error"
203 {
204 AString s2;
205 UtfCheck.PrintStatus(s2);
206 s.Add_Space_if_NotEmpty();
207 s += s2;
208 }
209 }
210 return s;
211 }
212
213
Open2(IInStream * stream,IArchiveOpenCallback * callback)214 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
215 {
216 UInt64 endPos;
217 {
218 RINOK(InStream_AtBegin_GetSize(stream, endPos))
219 }
220
221 _arc._phySize_Defined = true;
222
223 // bool utf8_OK = true;
224
225 _arc.SeqStream = stream;
226 _arc.InStream = stream;
227 _arc.OpenCallback = callback;
228
229 CItemEx item;
230 for (;;)
231 {
232 _arc.NumFiles = _items.Size();
233 RINOK(_arc.ReadItem(item))
234 if (!_arc.filled)
235 break;
236
237 _isArc = true;
238
239 /*
240 if (!_forceCodePage)
241 {
242 if (utf8_OK) utf8_OK = CheckUTF8(item.Name, item.NameCouldBeReduced);
243 if (utf8_OK) utf8_OK = CheckUTF8(item.LinkName, item.LinkNameCouldBeReduced);
244 if (utf8_OK) utf8_OK = CheckUTF8(item.User);
245 if (utf8_OK) utf8_OK = CheckUTF8(item.Group);
246 }
247 */
248
249 item.EncodingCharacts.Check(item.Name);
250 _encodingCharacts.Update(item.EncodingCharacts);
251
252 _items.Add(item);
253
254 RINOK(stream->Seek((Int64)item.Get_PackSize_Aligned(), STREAM_SEEK_CUR, &_arc._phySize))
255 if (_arc._phySize > endPos)
256 {
257 _arc._error = k_ErrorType_UnexpectedEnd;
258 break;
259 }
260 /*
261 if (_phySize == endPos)
262 {
263 _errorMessage = "There are no trailing zero-filled records";
264 break;
265 }
266 */
267 /*
268 if (callback)
269 {
270 if (_items.Size() == 1)
271 {
272 RINOK(callback->SetTotal(NULL, &endPos));
273 }
274 if ((_items.Size() & 0x3FF) == 0)
275 {
276 const UInt64 numFiles = _items.Size();
277 RINOK(callback->SetCompleted(&numFiles, &_phySize));
278 }
279 }
280 */
281 }
282
283 /*
284 if (!_forceCodePage)
285 {
286 if (!utf8_OK)
287 _curCodePage = k_DefaultCodePage;
288 }
289 */
290 _openCodePage = _curCodePage;
291
292 if (_items.Size() == 0)
293 {
294 if (_arc._error != k_ErrorType_OK)
295 {
296 _isArc = false;
297 return S_FALSE;
298 }
299 if (!callback)
300 return S_FALSE;
301 Z7_DECL_CMyComPtr_QI_FROM(
302 IArchiveOpenVolumeCallback,
303 openVolumeCallback, callback)
304 if (!openVolumeCallback)
305 return S_FALSE;
306 NCOM::CPropVariant prop;
307 if (openVolumeCallback->GetProperty(kpidName, &prop) != S_OK)
308 return S_FALSE;
309 if (prop.vt != VT_BSTR)
310 return S_FALSE;
311 unsigned len = MyStringLen(prop.bstrVal);
312 if (len < 4 || MyStringCompareNoCase(prop.bstrVal + len - 4, L".tar") != 0)
313 return S_FALSE;
314 }
315
316 _isArc = true;
317 return S_OK;
318 }
319
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback * openArchiveCallback))320 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback))
321 {
322 COM_TRY_BEGIN
323 // for (int i = 0; i < 10; i++) // for debug
324 {
325 Close();
326 RINOK(Open2(stream, openArchiveCallback))
327 _stream = stream;
328 }
329 return S_OK;
330 COM_TRY_END
331 }
332
Z7_COM7F_IMF(CHandler::OpenSeq (ISequentialInStream * stream))333 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
334 {
335 Close();
336 _seqStream = stream;
337 _isArc = true;
338 return S_OK;
339 }
340
Z7_COM7F_IMF(CHandler::Close ())341 Z7_COM7F_IMF(CHandler::Close())
342 {
343 _isArc = false;
344
345 _arc.Clear();
346
347 _curIndex = 0;
348 _latestIsRead = false;
349 _encodingCharacts.Clear();
350 _items.Clear();
351 _seqStream.Release();
352 _stream.Release();
353 return S_OK;
354 }
355
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))356 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
357 {
358 *numItems = (_stream ? _items.Size() : (UInt32)(Int32)-1);
359 return S_OK;
360 }
361
CHandler()362 CHandler::CHandler()
363 {
364 // copyCoder = new NCompress::CCopyCoder();
365 // copyCoder = copyCoder;
366 _openCodePage = CP_UTF8;
367 Init();
368 }
369
SkipTo(UInt32 index)370 HRESULT CHandler::SkipTo(UInt32 index)
371 {
372 while (_curIndex < index || !_latestIsRead)
373 {
374 if (_latestIsRead)
375 {
376 const UInt64 packSize = _latestItem.Get_PackSize_Aligned();
377 RINOK(copyCoder.Interface()->Code(_seqStream, NULL, &packSize, &packSize, NULL))
378 _arc._phySize += copyCoder->TotalSize;
379 if (copyCoder->TotalSize != packSize)
380 {
381 _arc._error = k_ErrorType_UnexpectedEnd;
382 return S_FALSE;
383 }
384 _latestIsRead = false;
385 _curIndex++;
386 }
387 else
388 {
389 _arc.SeqStream = _seqStream;
390 _arc.InStream = NULL;
391 RINOK(_arc.ReadItem(_latestItem))
392 if (!_arc.filled)
393 {
394 _arc._phySize_Defined = true;
395 return E_INVALIDARG;
396 }
397 _latestIsRead = true;
398 }
399 }
400 return S_OK;
401 }
402
TarStringToUnicode(const AString & s,NWindows::NCOM::CPropVariant & prop,bool toOs) const403 void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs) const
404 {
405 UString dest;
406 if (_curCodePage == CP_UTF8)
407 ConvertUTF8ToUnicode(s, dest);
408 else
409 MultiByteToUnicodeString2(dest, s, _curCodePage);
410 if (toOs)
411 NItemName::ReplaceToOsSlashes_Remove_TailSlash(dest,
412 true); // useBackslashReplacement
413 prop = dest;
414 }
415
416
417 // CPaxTime is defined (NumDigits >= 0)
PaxTimeToProp(const CPaxTime & pt,NWindows::NCOM::CPropVariant & prop)418 static void PaxTimeToProp(const CPaxTime &pt, NWindows::NCOM::CPropVariant &prop)
419 {
420 UInt64 v;
421 if (!NTime::UnixTime64_To_FileTime64(pt.Sec, v))
422 return;
423 if (pt.Ns != 0)
424 v += pt.Ns / 100;
425 FILETIME ft;
426 ft.dwLowDateTime = (DWORD)v;
427 ft.dwHighDateTime = (DWORD)(v >> 32);
428 prop.SetAsTimeFrom_FT_Prec_Ns100(ft,
429 k_PropVar_TimePrec_Base + (unsigned)pt.NumDigits, pt.Ns % 100);
430 }
431
432
AddSpecCharToString(const char c,AString & s)433 static void AddSpecCharToString(const char c, AString &s)
434 {
435 if ((Byte)c <= 0x20 || (Byte)c > 127)
436 {
437 s.Add_Char('[');
438 s.Add_Char(GET_HEX_CHAR_LOWER((Byte)c >> 4));
439 s.Add_Char(GET_HEX_CHAR_LOWER(c & 15));
440 s.Add_Char(']');
441 }
442 else
443 s.Add_Char(c);
444 }
445
AddSpecUInt64(AString & s,const char * name,UInt64 v)446 static void AddSpecUInt64(AString &s, const char *name, UInt64 v)
447 {
448 if (v != 0)
449 {
450 s.Add_OptSpaced(name);
451 if (v > 1)
452 {
453 s.Add_Colon();
454 s.Add_UInt64(v);
455 }
456 }
457 }
458
AddSpecBools(AString & s,const char * name,bool b1,bool b2)459 static void AddSpecBools(AString &s, const char *name, bool b1, bool b2)
460 {
461 if (b1)
462 {
463 s.Add_OptSpaced(name);
464 if (b2)
465 s.Add_Char('*');
466 }
467 }
468
469
470 #if 0
Parse_Attrib_from_SCHILY_fflags(const AString & s,UInt32 & attribRes)471 static bool Parse_Attrib_from_SCHILY_fflags(const AString &s, UInt32 &attribRes)
472 {
473 UInt32 attrib = 0;
474 attribRes = attrib;
475 unsigned pos = 0;
476 while (pos < s.Len())
477 {
478 int pos2 = s.Find(',', pos);
479 if (pos2 < 0)
480 pos2 = (int)s.Len();
481 const AString str = s.Mid(pos, (unsigned)pos2 - pos);
482 if (str.IsEqualTo("hidden")) attrib |= FILE_ATTRIBUTE_HIDDEN;
483 else if (str.IsEqualTo("rdonly")) attrib |= FILE_ATTRIBUTE_READONLY;
484 else if (str.IsEqualTo("system")) attrib |= FILE_ATTRIBUTE_SYSTEM;
485 else
486 return false;
487 pos = (unsigned)pos2 + 1;
488 }
489 attribRes = attrib;
490 return true;
491 }
492 #endif
493
494
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))495 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
496 {
497 COM_TRY_BEGIN
498 NCOM::CPropVariant prop;
499
500 const CItemEx *item;
501 if (_stream)
502 item = &_items[index];
503 else
504 {
505 if (index < _curIndex)
506 return E_INVALIDARG;
507 else
508 {
509 RINOK(SkipTo(index))
510 item = &_latestItem;
511 }
512 }
513
514 switch (propID)
515 {
516 case kpidPath: TarStringToUnicode(item->Name, prop, true); break;
517 case kpidIsDir: prop = item->IsDir(); break;
518 case kpidSize: prop = item->Get_UnpackSize(); break;
519 case kpidPackSize: prop = item->Get_PackSize_Aligned(); break;
520 case kpidMTime:
521 {
522 /*
523 // for debug:
524 PropVariant_SetFrom_UnixTime(prop, 1 << 30);
525 prop.wReserved1 = k_PropVar_TimePrec_Base + 1;
526 prop.wReserved2 = 12;
527 break;
528 */
529
530 if (item->PaxTimes.MTime.IsDefined())
531 PaxTimeToProp(item->PaxTimes.MTime, prop);
532 else
533 // if (item->MTime != 0)
534 {
535 // we allow (item->MTime == 0)
536 FILETIME ft;
537 if (NTime::UnixTime64_To_FileTime(item->MTime, ft))
538 {
539 unsigned prec = k_PropVar_TimePrec_Unix;
540 if (item->MTime_IsBin)
541 {
542 /* we report here that it's Int64-UnixTime
543 instead of basic UInt32-UnixTime range */
544 prec = k_PropVar_TimePrec_Base;
545 }
546 prop.SetAsTimeFrom_FT_Prec(ft, prec);
547 }
548 }
549 break;
550 }
551 case kpidATime:
552 if (item->PaxTimes.ATime.IsDefined())
553 PaxTimeToProp(item->PaxTimes.ATime, prop);
554 break;
555 case kpidCTime:
556 if (item->PaxTimes.CTime.IsDefined())
557 PaxTimeToProp(item->PaxTimes.CTime, prop);
558 break;
559 case kpidPosixAttrib: prop = item->Get_Combined_Mode(); break;
560
561 // kpidAttrib has priority over kpidPosixAttrib in 7-Zip.
562 // but if we want kpidPosixAttrib priority for TAR, we disable kpidAttrib.
563 #if 0
564 case kpidAttrib:
565 {
566 if (!item->SCHILY_fflags.IsEmpty())
567 {
568 UInt32 attrib = 0;
569 if (Parse_Attrib_from_SCHILY_fflags(item->SCHILY_fflags, attrib))
570 {
571 if (attrib != 0)
572 {
573 if (item->IsDir())
574 attrib |= FILE_ATTRIBUTE_DIRECTORY;
575 attrib |= ((UInt32)item->Get_Combined_Mode() << 16) | 0x8000; // FILE_ATTRIBUTE_UNIX_EXTENSION;
576 prop = attrib;
577 }
578 }
579 }
580 break;
581 }
582 #endif
583
584 case kpidUser:
585 if (!item->User.IsEmpty())
586 TarStringToUnicode(item->User, prop);
587 break;
588 case kpidGroup:
589 if (!item->Group.IsEmpty())
590 TarStringToUnicode(item->Group, prop);
591 break;
592
593 case kpidUserId:
594 // if (item->UID != 0)
595 prop = (UInt32)item->UID;
596 break;
597 case kpidGroupId:
598 // if (item->GID != 0)
599 prop = (UInt32)item->GID;
600 break;
601
602 case kpidDeviceMajor:
603 if (item->DeviceMajor_Defined)
604 // if (item->DeviceMajor != 0)
605 prop = (UInt32)item->DeviceMajor;
606 break;
607
608 case kpidDeviceMinor:
609 if (item->DeviceMinor_Defined)
610 // if (item->DeviceMinor != 0)
611 prop = (UInt32)item->DeviceMinor;
612 break;
613 /*
614 case kpidDevice:
615 if (item->DeviceMajor_Defined)
616 if (item->DeviceMinor_Defined)
617 prop = (UInt64)MY_dev_makedev(item->DeviceMajor, item->DeviceMinor);
618 break;
619 */
620
621 case kpidSymLink:
622 if (item->Is_SymLink())
623 if (!item->LinkName.IsEmpty())
624 TarStringToUnicode(item->LinkName, prop);
625 break;
626 case kpidHardLink:
627 if (item->Is_HardLink())
628 if (!item->LinkName.IsEmpty())
629 TarStringToUnicode(item->LinkName, prop);
630 break;
631
632 case kpidCharacts:
633 {
634 AString s;
635 {
636 s.Add_Space_if_NotEmpty();
637 AddSpecCharToString(item->LinkFlag, s);
638 }
639 if (item->IsMagic_GNU())
640 s.Add_OptSpaced("GNU");
641 else if (item->IsMagic_Posix_ustar_00())
642 s.Add_OptSpaced("POSIX");
643 else
644 {
645 s.Add_Space_if_NotEmpty();
646 for (unsigned i = 0; i < sizeof(item->Magic); i++)
647 AddSpecCharToString(item->Magic[i], s);
648 }
649
650 if (item->IsSignedChecksum)
651 s.Add_OptSpaced("SignedChecksum");
652
653 if (item->Prefix_WasUsed)
654 s.Add_OptSpaced(k_Characts_Prefix);
655
656 s.Add_OptSpaced(item->EncodingCharacts.GetCharactsString());
657
658 // AddSpecUInt64(s, "LongName", item->Num_LongName_Records);
659 // AddSpecUInt64(s, "LongLink", item->Num_LongLink_Records);
660 AddSpecBools(s, "LongName", item->LongName_WasUsed, item->LongName_WasUsed_2);
661 AddSpecBools(s, "LongLink", item->LongLink_WasUsed, item->LongLink_WasUsed_2);
662
663 if (item->MTime_IsBin)
664 s.Add_OptSpaced("bin_mtime");
665 if (item->PackSize_IsBin)
666 s.Add_OptSpaced("bin_psize");
667 if (item->Size_IsBin)
668 s.Add_OptSpaced("bin_size");
669
670 AddSpecUInt64(s, "PAX", item->Num_Pax_Records);
671
672 if (item->PaxTimes.MTime.IsDefined()) s.Add_OptSpaced("mtime");
673 if (item->PaxTimes.ATime.IsDefined()) s.Add_OptSpaced("atime");
674 if (item->PaxTimes.CTime.IsDefined()) s.Add_OptSpaced("ctime");
675
676 if (item->pax_path_WasUsed)
677 s.Add_OptSpaced("pax_path");
678 if (item->pax_link_WasUsed)
679 s.Add_OptSpaced("pax_linkpath");
680 if (item->pax_size_WasUsed)
681 s.Add_OptSpaced("pax_size");
682 if (!item->SCHILY_fflags.IsEmpty())
683 {
684 s.Add_OptSpaced("SCHILY.fflags=");
685 s += item->SCHILY_fflags;
686 }
687 if (item->IsThereWarning())
688 s.Add_OptSpaced("WARNING");
689 if (item->HeaderError)
690 s.Add_OptSpaced("ERROR");
691 if (item->Pax_Error)
692 s.Add_OptSpaced("PAX_error");
693 if (!item->PaxExtra.RawLines.IsEmpty())
694 s.Add_OptSpaced("PAX_unsupported_line");
695 if (item->Pax_Overflow)
696 s.Add_OptSpaced("PAX_overflow");
697 if (!s.IsEmpty())
698 prop = s;
699 break;
700 }
701 case kpidComment:
702 {
703 AString s;
704 item->PaxExtra.Print_To_String(s);
705 if (!s.IsEmpty())
706 prop = s;
707 break;
708 }
709 // case kpidHeadersSize: prop = item->HeaderSize; break; // for debug
710 // case kpidOffset: prop = item->HeaderPos; break; // for debug
711 default: break;
712 }
713 prop.Detach(value);
714 return S_OK;
715 COM_TRY_END
716 }
717
718
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))719 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
720 Int32 testMode, IArchiveExtractCallback *extractCallback))
721 {
722 COM_TRY_BEGIN
723 ISequentialInStream *stream = _seqStream;
724 const bool seqMode = (_stream == NULL);
725 if (!seqMode)
726 stream = _stream;
727
728 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
729 if (allFilesMode)
730 numItems = _items.Size();
731 if (_stream && numItems == 0)
732 return S_OK;
733 UInt64 totalSize = 0;
734 UInt32 i;
735 for (i = 0; i < numItems; i++)
736 totalSize += _items[allFilesMode ? i : indices[i]].Get_UnpackSize();
737 RINOK(extractCallback->SetTotal(totalSize))
738
739 UInt64 totalPackSize;
740 totalSize = totalPackSize = 0;
741
742 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
743 lps->Init(extractCallback, false);
744 CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
745 inStream->SetStream(stream);
746 CMyComPtr2_Create<ISequentialOutStream, CLimitedSequentialOutStream> outStreamSpec;
747
748 for (i = 0; ; i++)
749 {
750 lps->InSize = totalPackSize;
751 lps->OutSize = totalSize;
752 RINOK(lps->SetCur())
753 if (i >= numItems && !seqMode)
754 break;
755 const UInt32 index = allFilesMode ? i : indices[i];
756 const CItemEx *item;
757 if (seqMode)
758 {
759 const HRESULT res = SkipTo(index);
760 if (res == E_INVALIDARG)
761 break;
762 RINOK(res)
763 item = &_latestItem;
764 }
765 else
766 item = &_items[index];
767
768 Int32 askMode = testMode ?
769 NExtract::NAskMode::kTest :
770 NExtract::NAskMode::kExtract;
771 CMyComPtr<ISequentialOutStream> realOutStream;
772 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
773 const UInt64 unpackSize = item->Get_UnpackSize();
774 totalSize += unpackSize;
775 totalPackSize += item->Get_PackSize_Aligned();
776 if (item->IsDir())
777 {
778 RINOK(extractCallback->PrepareOperation(askMode))
779 // realOutStream.Release();
780 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
781 continue;
782 }
783 bool skipMode = false;
784 if (!testMode && !realOutStream)
785 {
786 if (!seqMode)
787 {
788 /*
789 // GetStream() creates link.
790 // so we can show extracting info in GetStream() instead
791 if (item->Is_HardLink() ||
792 item->Is_SymLink())
793 {
794 RINOK(extractCallback->PrepareOperation(askMode))
795 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
796 }
797 */
798 continue;
799 }
800 skipMode = true;
801 askMode = NExtract::NAskMode::kSkip;
802 }
803 RINOK(extractCallback->PrepareOperation(askMode))
804
805 outStreamSpec->SetStream(realOutStream);
806 realOutStream.Release();
807 outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
808
809 Int32 opRes = NExtract::NOperationResult::kOK;
810 CMyComPtr<ISequentialInStream> inStream2;
811 if (!item->Is_Sparse())
812 inStream2 = inStream;
813 else
814 {
815 GetStream(index, &inStream2);
816 if (!inStream2)
817 return E_FAIL;
818 }
819
820 {
821 if (item->Is_SymLink())
822 {
823 RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len()))
824 }
825 else
826 {
827 if (!seqMode)
828 {
829 RINOK(InStream_SeekSet(_stream, item->Get_DataPos()))
830 }
831 inStream->Init(item->Get_PackSize_Aligned());
832 RINOK(copyCoder.Interface()->Code(inStream2, outStreamSpec, NULL, NULL, lps))
833 }
834 if (outStreamSpec->GetRem() != 0)
835 opRes = NExtract::NOperationResult::kDataError;
836 }
837 if (seqMode)
838 {
839 _latestIsRead = false;
840 _curIndex++;
841 }
842 outStreamSpec->ReleaseStream();
843 RINOK(extractCallback->SetOperationResult(opRes))
844 }
845 return S_OK;
846 COM_TRY_END
847 }
848
849
850 Z7_CLASS_IMP_IInStream(
851 CSparseStream
852 )
853 UInt64 _phyPos;
854 UInt64 _virtPos;
855 bool _needStartSeek;
856
857 public:
858 CHandler *Handler;
859 CMyComPtr<IUnknown> HandlerRef;
860 unsigned ItemIndex;
861 CRecordVector<UInt64> PhyOffsets;
862
863 void Init()
864 {
865 _virtPos = 0;
866 _phyPos = 0;
867 _needStartSeek = true;
868 }
869 };
870
871
872 Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize))
873 {
874 if (processedSize)
875 *processedSize = 0;
876 if (size == 0)
877 return S_OK;
878 const CItemEx &item = Handler->_items[ItemIndex];
879 if (_virtPos >= item.Size)
880 return S_OK;
881 {
882 UInt64 rem = item.Size - _virtPos;
883 if (size > rem)
884 size = (UInt32)rem;
885 }
886
887 HRESULT res = S_OK;
888
889 if (item.SparseBlocks.IsEmpty())
890 memset(data, 0, size);
891 else
892 {
893 unsigned left = 0, right = item.SparseBlocks.Size();
894 for (;;)
895 {
896 const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
897 if (mid == left)
898 break;
899 if (_virtPos < item.SparseBlocks[mid].Offset)
900 right = mid;
901 else
902 left = mid;
903 }
904
905 const CSparseBlock &sb = item.SparseBlocks[left];
906 UInt64 relat = _virtPos - sb.Offset;
907
908 if (_virtPos >= sb.Offset && relat < sb.Size)
909 {
910 UInt64 rem = sb.Size - relat;
911 if (size > rem)
912 size = (UInt32)rem;
913 UInt64 phyPos = PhyOffsets[left] + relat;
914 if (_needStartSeek || _phyPos != phyPos)
915 {
916 RINOK(InStream_SeekSet(Handler->_stream, (item.Get_DataPos() + phyPos)))
917 _needStartSeek = false;
918 _phyPos = phyPos;
919 }
920 res = Handler->_stream->Read(data, size, &size);
921 _phyPos += size;
922 }
923 else
924 {
925 UInt64 next = item.Size;
926 if (_virtPos < sb.Offset)
927 next = sb.Offset;
928 else if (left + 1 < item.SparseBlocks.Size())
929 next = item.SparseBlocks[left + 1].Offset;
930 UInt64 rem = next - _virtPos;
931 if (size > rem)
932 size = (UInt32)rem;
933 memset(data, 0, size);
934 }
935 }
936
937 _virtPos += size;
938 if (processedSize)
939 *processedSize = size;
940 return res;
941 }
942
943 Z7_COM7F_IMF(CSparseStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
944 {
945 switch (seekOrigin)
946 {
947 case STREAM_SEEK_SET: break;
948 case STREAM_SEEK_CUR: offset += _virtPos; break;
949 case STREAM_SEEK_END: offset += Handler->_items[ItemIndex].Size; break;
950 default: return STG_E_INVALIDFUNCTION;
951 }
952 if (offset < 0)
953 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
954 _virtPos = (UInt64)offset;
955 if (newPosition)
956 *newPosition = _virtPos;
957 return S_OK;
958 }
959
960 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
961 {
962 COM_TRY_BEGIN
963
964 const CItemEx &item = _items[index];
965
966 if (item.Is_Sparse())
967 {
968 CSparseStream *streamSpec = new CSparseStream;
969 CMyComPtr<IInStream> streamTemp = streamSpec;
970 streamSpec->Init();
971 streamSpec->Handler = this;
972 streamSpec->HandlerRef = (IInArchive *)this;
973 streamSpec->ItemIndex = index;
974 streamSpec->PhyOffsets.Reserve(item.SparseBlocks.Size());
975 UInt64 offs = 0;
976 FOR_VECTOR(i, item.SparseBlocks)
977 {
978 const CSparseBlock &sb = item.SparseBlocks[i];
979 streamSpec->PhyOffsets.AddInReserved(offs);
980 offs += sb.Size;
981 }
982 *stream = streamTemp.Detach();
983 return S_OK;
984 }
985
986 if (item.Is_SymLink())
987 {
988 Create_BufInStream_WithReference((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this, stream);
989 return S_OK;
990 }
991
992 return CreateLimitedInStream(_stream, item.Get_DataPos(), item.PackSize, stream);
993
994 COM_TRY_END
995 }
996
997
998 void CHandler::Init()
999 {
1000 _forceCodePage = false;
1001 _curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP;
1002 _posixMode = false;
1003 _posixMode_WasForced = false;
1004 // TimeOptions.Clear();
1005 _handlerTimeOptions.Init();
1006 // _handlerTimeOptions.Write_MTime.Val = true; // it's default already
1007 }
1008
1009
1010 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
1011 {
1012 Init();
1013
1014 for (UInt32 i = 0; i < numProps; i++)
1015 {
1016 UString name = names[i];
1017 name.MakeLower_Ascii();
1018 if (name.IsEmpty())
1019 return E_INVALIDARG;
1020
1021 const PROPVARIANT &prop = values[i];
1022
1023 if (name[0] == L'x')
1024 {
1025 // some clients write 'x' property. So we support it
1026 UInt32 level = 0;
1027 RINOK(ParsePropToUInt32(name.Ptr(1), prop, level))
1028 }
1029 else if (name.IsEqualTo("cp"))
1030 {
1031 UInt32 cp = CP_OEMCP;
1032 RINOK(ParsePropToUInt32(L"", prop, cp))
1033 _forceCodePage = true;
1034 _curCodePage = _specifiedCodePage = cp;
1035 }
1036 else if (name.IsPrefixedBy_Ascii_NoCase("mt"))
1037 {
1038 }
1039 else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
1040 {
1041 }
1042 else if (name.IsEqualTo("m"))
1043 {
1044 if (prop.vt != VT_BSTR)
1045 return E_INVALIDARG;
1046 const UString s = prop.bstrVal;
1047 if (s.IsEqualTo_Ascii_NoCase("pax") ||
1048 s.IsEqualTo_Ascii_NoCase("posix"))
1049 _posixMode = true;
1050 else if (s.IsEqualTo_Ascii_NoCase("gnu"))
1051 _posixMode = false;
1052 else
1053 return E_INVALIDARG;
1054 _posixMode_WasForced = true;
1055 }
1056 else
1057 {
1058 /*
1059 if (name.IsPrefixedBy_Ascii_NoCase("td"))
1060 {
1061 name.Delete(0, 3);
1062 if (prop.vt == VT_EMPTY)
1063 {
1064 if (name.IsEqualTo_Ascii_NoCase("n"))
1065 {
1066 // TimeOptions.UseNativeDigits = true;
1067 }
1068 else if (name.IsEqualTo_Ascii_NoCase("r"))
1069 {
1070 // TimeOptions.RemoveZeroDigits = true;
1071 }
1072 else
1073 return E_INVALIDARG;
1074 }
1075 else
1076 {
1077 UInt32 numTimeDigits = 0;
1078 RINOK(ParsePropToUInt32(name, prop, numTimeDigits));
1079 TimeOptions.NumDigits_WasForced = true;
1080 TimeOptions.NumDigits = numTimeDigits;
1081 }
1082 }
1083 */
1084 bool processed = false;
1085 RINOK(_handlerTimeOptions.Parse(name, prop, processed))
1086 if (processed)
1087 continue;
1088 return E_INVALIDARG;
1089 }
1090 }
1091 return S_OK;
1092 }
1093
1094 }}
1095