xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Tar/TarHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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