xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/Tar/TarIn.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // TarIn.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../../C/CpuArch.h"
6 
7 #include "../../../Common/StringToInt.h"
8 
9 #include "../../Common/StreamUtils.h"
10 
11 #include "../IArchive.h"
12 
13 #include "TarIn.h"
14 
15 #define NUM_UNROLL_BYTES (8 * 4)
16 
17 Z7_NO_INLINE static bool IsBufNonZero(const void *data, size_t size);
IsBufNonZero(const void * data,size_t size)18 Z7_NO_INLINE static bool IsBufNonZero(const void *data, size_t size)
19 {
20   const Byte *p = (const Byte *)data;
21 
22   for (; size != 0 && ((unsigned)(ptrdiff_t)p & (NUM_UNROLL_BYTES - 1)) != 0; size--)
23     if (*p++ != 0)
24       return true;
25 
26   if (size >= NUM_UNROLL_BYTES)
27   {
28     const Byte *lim = p + size;
29     size &= (NUM_UNROLL_BYTES - 1);
30     lim -= size;
31     do
32     {
33       if (*(const UInt64 *)(const void *)(p        ) != 0) return true;
34       if (*(const UInt64 *)(const void *)(p + 8 * 1) != 0) return true;
35       if (*(const UInt64 *)(const void *)(p + 8 * 2) != 0) return true;
36       if (*(const UInt64 *)(const void *)(p + 8 * 3) != 0) return true;
37       // if (*(const UInt32 *)(const void *)(p        ) != 0) return true;
38       // if (*(const UInt32 *)(const void *)(p + 4 * 1) != 0) return true;
39       // if (*(const UInt32 *)(const void *)(p + 4 * 2) != 0) return true;
40       // if (*(const UInt32 *)(const void *)(p + 4 * 3) != 0) return true;
41       p += NUM_UNROLL_BYTES;
42     }
43     while (p != lim);
44   }
45 
46   for (; size != 0; size--)
47     if (*p++ != 0)
48       return true;
49 
50   return false;
51 }
52 
53 
54 namespace NArchive {
55 namespace NTar {
56 
MyStrNCpy(char * dest,const char * src,unsigned size)57 static void MyStrNCpy(char *dest, const char *src, unsigned size)
58 {
59   for (unsigned i = 0; i < size; i++)
60   {
61     char c = src[i];
62     dest[i] = c;
63     if (c == 0)
64       break;
65   }
66 }
67 
OctalToNumber(const char * srcString,unsigned size,UInt64 & res,bool allowEmpty=false)68 static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res, bool allowEmpty = false)
69 {
70   res = 0;
71   char sz[32];
72   MyStrNCpy(sz, srcString, size);
73   sz[size] = 0;
74   const char *end;
75   unsigned i;
76   for (i = 0; sz[i] == ' '; i++);
77   if (sz[i] == 0)
78     return allowEmpty;
79   res = ConvertOctStringToUInt64(sz + i, &end);
80   return (*end == ' ' || *end == 0);
81 }
82 
OctalToNumber32(const char * srcString,UInt32 & res,bool allowEmpty=false)83 static bool OctalToNumber32(const char *srcString, UInt32 &res, bool allowEmpty = false)
84 {
85   const unsigned kSize = 8;
86   UInt64 res64;
87   if (!OctalToNumber(srcString, kSize, res64, allowEmpty))
88     return false;
89   res = (UInt32)res64;
90   return (res64 <= 0xFFFFFFFF);
91 }
92 
93 #define RIF(x) { if (!(x)) return S_OK; }
94 
ReadString(const char * s,unsigned size,AString & result)95 static void ReadString(const char *s, unsigned size, AString &result)
96 {
97   result.SetFrom_CalcLen(s, size);
98 }
99 
ParseInt64(const char * p,Int64 & val,bool & isBin)100 static bool ParseInt64(const char *p, Int64 &val, bool &isBin)
101 {
102   const UInt32 h = GetBe32(p);
103   val = (Int64)GetBe64(p + 4);
104   isBin = true;
105   if (h == (UInt32)1 << 31)
106     return ((val >> 63) & 1) == 0;
107   if (h == (UInt32)(Int32)-1)
108     return ((val >> 63) & 1) != 0;
109   isBin = false;
110   UInt64 u;
111   const bool res = OctalToNumber(p, 12, u);
112   val = (Int64)u;
113   return res;
114 }
115 
ParseInt64_MTime(const char * p,Int64 & val,bool & isBin)116 static bool ParseInt64_MTime(const char *p, Int64 &val, bool &isBin)
117 {
118   // rare case tar : ZEROs in Docker-Windows TARs
119   // rare case tar : spaces
120   isBin = false;
121   if (GetUi32(p) != 0)
122   for (unsigned i = 0; i < 12; i++)
123     if (p[i] != ' ')
124       return ParseInt64(p, val, isBin);
125   val = 0;
126   return true;
127 }
128 
ParseSize(const char * p,UInt64 & val,bool & isBin)129 static bool ParseSize(const char *p, UInt64 &val, bool &isBin)
130 {
131   if (GetBe32(p) == (UInt32)1 << 31)
132   {
133     // GNU extension
134     isBin = true;
135     val = GetBe64(p + 4);
136     return ((val >> 63) & 1) == 0;
137   }
138   isBin = false;
139   return OctalToNumber(p, 12, val,
140       true // 20.03: allow empty size for 'V' Label entry
141       );
142 }
143 
ParseSize(const char * p,UInt64 & val)144 static bool ParseSize(const char *p, UInt64 &val)
145 {
146   bool isBin;
147   return ParseSize(p, val, isBin);
148 }
149 
150 #define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
151 
IsArc_Tar(const Byte * p2,size_t size)152 API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
153 {
154   if (size < NFileHeader::kRecordSize)
155     return k_IsArc_Res_NEED_MORE;
156 
157   const char *p = (const char *)p2;
158   p += NFileHeader::kNameSize;
159 
160   UInt32 mode;
161   // we allow empty Mode value for LongName prefix items
162   CHECK(OctalToNumber32(p, mode, true)) p += 8;
163 
164   // if (!OctalToNumber32(p, item.UID)) item.UID = 0;
165   p += 8;
166   // if (!OctalToNumber32(p, item.GID)) item.GID = 0;
167   p += 8;
168 
169   UInt64 packSize;
170   Int64 time;
171   UInt32 checkSum;
172   bool isBin;
173   CHECK(ParseSize(p, packSize, isBin)) p += 12;
174   CHECK(ParseInt64_MTime(p, time, isBin)) p += 12;
175   CHECK(OctalToNumber32(p, checkSum))
176   return k_IsArc_Res_YES;
177 }
178 
179 
GetNextItemReal(CItemEx & item)180 HRESULT CArchive::GetNextItemReal(CItemEx &item)
181 {
182   char buf[NFileHeader::kRecordSize];
183 
184   error = k_ErrorType_OK;
185   filled = false;
186 
187   bool thereAreEmptyRecords = false;
188   for (;;)
189   {
190     size_t processedSize = NFileHeader::kRecordSize;
191     RINOK(ReadStream(SeqStream, buf, &processedSize))
192     if (processedSize == 0)
193     {
194       if (!thereAreEmptyRecords)
195         error = k_ErrorType_UnexpectedEnd; // "There are no trailing zero-filled records";
196       return S_OK;
197     }
198     if (processedSize != NFileHeader::kRecordSize)
199     {
200       if (!thereAreEmptyRecords)
201         error = k_ErrorType_UnexpectedEnd; // error = "There is no correct record at the end of archive";
202       else
203       {
204         /*
205         if (IsEmptyData(buf, processedSize))
206           error = k_ErrorType_UnexpectedEnd;
207         else
208         {
209           // extraReadSize = processedSize;
210           // error = k_ErrorType_Corrupted; // some data after the end tail zeros
211         }
212         */
213       }
214 
215       return S_OK;
216     }
217     if (IsBufNonZero(buf, NFileHeader::kRecordSize))
218       break;
219     item.HeaderSize += NFileHeader::kRecordSize;
220     thereAreEmptyRecords = true;
221     if (OpenCallback)
222     {
223       RINOK(Progress(item, 0))
224     }
225   }
226   if (thereAreEmptyRecords)
227   {
228     // error = "There are data after end of archive";
229     return S_OK;
230   }
231 
232   char *p = buf;
233 
234   error = k_ErrorType_Corrupted;
235 
236   // ReadString(p, NFileHeader::kNameSize, item.Name);
237   p += NFileHeader::kNameSize;
238 
239   /*
240   item.Name_CouldBeReduced =
241       (item.Name.Len() == NFileHeader::kNameSize ||
242        item.Name.Len() == NFileHeader::kNameSize - 1);
243   */
244 
245   // we allow empty Mode value for LongName prefix items
246   RIF(OctalToNumber32(p, item.Mode, true)) p += 8;
247 
248   if (!OctalToNumber32(p, item.UID)) { item.UID = 0; }  p += 8;
249   if (!OctalToNumber32(p, item.GID)) { item.GID = 0; }  p += 8;
250 
251   RIF(ParseSize(p, item.PackSize, item.PackSize_IsBin))
252   item.Size = item.PackSize;
253   item.Size_IsBin = item.PackSize_IsBin;
254   p += 12;
255   RIF(ParseInt64_MTime(p, item.MTime, item.MTime_IsBin)) p += 12;
256 
257   UInt32 checkSum;
258   RIF(OctalToNumber32(p, checkSum))
259   memset(p, ' ', 8); p += 8;
260 
261   item.LinkFlag = *p++;
262 
263   ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
264 
265   /*
266   item.LinkName_CouldBeReduced =
267       (item.LinkName.Len() == NFileHeader::kNameSize ||
268        item.LinkName.Len() == NFileHeader::kNameSize - 1);
269   */
270 
271   memcpy(item.Magic, p, 8); p += 8;
272 
273   ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
274   ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
275 
276   item.DeviceMajor_Defined = (p[0] != 0); if (item.DeviceMajor_Defined) { RIF(OctalToNumber32(p, item.DeviceMajor)) } p += 8;
277   item.DeviceMinor_Defined = (p[0] != 0); if (item.DeviceMinor_Defined) { RIF(OctalToNumber32(p, item.DeviceMinor)) } p += 8;
278 
279   if (p[0] != 0
280       && item.IsMagic_ustar_5chars()
281       && (item.LinkFlag != 'L' ))
282   {
283     item.Prefix_WasUsed = true;
284     ReadString(p, NFileHeader::kPrefixSize, item.Name);
285     item.Name.Add_Slash();
286     unsigned i;
287     for (i = 0; i < NFileHeader::kNameSize; i++)
288       if (buf[i] == 0)
289         break;
290     item.Name.AddFrom(buf, i);
291   }
292   else
293     ReadString(buf, NFileHeader::kNameSize, item.Name);
294 
295   p += NFileHeader::kPrefixSize;
296 
297   if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
298   {
299     item.PackSize = 0;
300     item.Size = 0;
301   }
302 
303   if (item.LinkFlag == NFileHeader::NLinkFlag::kDirectory)
304   {
305     // GNU tar ignores Size field, if LinkFlag is kDirectory
306     // 21.02 : we set PackSize = 0 to be more compatible with GNU tar
307     item.PackSize = 0;
308     // item.Size = 0;
309   }
310 
311   /*
312     TAR standard requires sum of unsigned byte values.
313     But some old TAR programs use sum of signed byte values.
314     So we check both values.
315   */
316   // for (int y = 0; y < 100; y++) // for debug
317   {
318     UInt32 sum0 = 0;
319     {
320       for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
321         sum0 += (Byte)buf[i];
322     }
323     if (sum0 != checkSum)
324     {
325       Int32 sum = 0;
326       for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
327         sum += (signed char)buf[i];
328       if ((UInt32)sum != checkSum)
329         return S_OK;
330       item.IsSignedChecksum = true;
331     }
332   }
333 
334   item.HeaderSize += NFileHeader::kRecordSize;
335 
336   if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse)
337   {
338     Byte isExtended = (Byte)buf[482];
339     if (isExtended != 0 && isExtended != 1)
340       return S_OK;
341     RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin))
342     UInt64 min = 0;
343     for (unsigned i = 0; i < 4; i++)
344     {
345       p = buf + 386 + 24 * i;
346       if (GetBe32(p) == 0)
347       {
348         if (isExtended != 0)
349           return S_OK;
350         break;
351       }
352       CSparseBlock sb;
353       RIF(ParseSize(p, sb.Offset))
354       RIF(ParseSize(p + 12, sb.Size))
355       item.SparseBlocks.Add(sb);
356       if (sb.Offset < min || sb.Offset > item.Size)
357         return S_OK;
358       if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
359         return S_OK;
360       min = sb.Offset + sb.Size;
361       if (min < sb.Offset)
362         return S_OK;
363     }
364     if (min > item.Size)
365       return S_OK;
366 
367     while (isExtended != 0)
368     {
369       size_t processedSize = NFileHeader::kRecordSize;
370       RINOK(ReadStream(SeqStream, buf, &processedSize))
371       if (processedSize != NFileHeader::kRecordSize)
372       {
373         error = k_ErrorType_UnexpectedEnd;
374         return S_OK;
375       }
376 
377       item.HeaderSize += NFileHeader::kRecordSize;
378 
379       if (OpenCallback)
380       {
381         RINOK(Progress(item, 0))
382       }
383 
384       isExtended = (Byte)buf[21 * 24];
385       if (isExtended != 0 && isExtended != 1)
386         return S_OK;
387       for (unsigned i = 0; i < 21; i++)
388       {
389         p = buf + 24 * i;
390         if (GetBe32(p) == 0)
391         {
392           if (isExtended != 0)
393             return S_OK;
394           break;
395         }
396         CSparseBlock sb;
397         RIF(ParseSize(p, sb.Offset))
398         RIF(ParseSize(p + 12, sb.Size))
399         item.SparseBlocks.Add(sb);
400         if (sb.Offset < min || sb.Offset > item.Size)
401           return S_OK;
402         if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
403           return S_OK;
404         min = sb.Offset + sb.Size;
405         if (min < sb.Offset)
406           return S_OK;
407       }
408     }
409     if (min > item.Size)
410       return S_OK;
411   }
412 
413   if (item.PackSize >= (UInt64)1 << 63)
414     return S_OK;
415 
416   filled = true;
417   error = k_ErrorType_OK;
418   return S_OK;
419 }
420 
421 
Progress(const CItemEx & item,UInt64 posOffset)422 HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset)
423 {
424   const UInt64 pos = item.Get_DataPos() + posOffset;
425   if (NumFiles - NumFiles_Prev < (1 << 16)
426       // && NumRecords - NumRecords_Prev < (1 << 16)
427       && pos - Pos_Prev < ((UInt32)1 << 28))
428     return S_OK;
429   {
430     Pos_Prev = pos;
431     NumFiles_Prev = NumFiles;
432     // NumRecords_Prev = NumRecords;
433     // Sleep(100); // for debug
434     return OpenCallback->SetCompleted(&NumFiles, &pos);
435   }
436 }
437 
438 
ReadDataToBuffer(const CItemEx & item,CTempBuffer & tb,size_t stringLimit)439 HRESULT CArchive::ReadDataToBuffer(const CItemEx &item,
440     CTempBuffer &tb, size_t stringLimit)
441 {
442   tb.Init();
443   UInt64 packSize = item.Get_PackSize_Aligned();
444   if (packSize == 0)
445     return S_OK;
446 
447   UInt64 pos;
448 
449   {
450     size_t size = stringLimit;
451     if (size > packSize)
452       size = (size_t)packSize;
453     tb.Buffer.AllocAtLeast(size);
454     size_t processedSize = size;
455     const HRESULT res = ReadStream(SeqStream, tb.Buffer, &processedSize);
456     pos = processedSize;
457     if (processedSize != size)
458     {
459       error = k_ErrorType_UnexpectedEnd;
460       return res;
461     }
462     RINOK(res)
463 
464     packSize -= size;
465 
466     size_t i;
467     const Byte *p = tb.Buffer;
468     for (i = 0; i < size; i++)
469       if (p[i] == 0)
470         break;
471     if (i >= item.PackSize)
472       tb.StringSize_IsConfirmed = true;
473     if (i > item.PackSize)
474     {
475       tb.StringSize = (size_t)item.PackSize;
476       tb.IsNonZeroTail = true;
477     }
478     else
479     {
480       tb.StringSize = i;
481       if (i != size)
482       {
483         tb.StringSize_IsConfirmed = true;
484         if (IsBufNonZero(p + i, size - i))
485           tb.IsNonZeroTail = true;
486       }
487     }
488 
489     if (packSize == 0)
490       return S_OK;
491   }
492 
493   if (InStream)
494   {
495     RINOK(InStream->Seek((Int64)packSize, STREAM_SEEK_CUR, NULL))
496     return S_OK;
497   }
498   const unsigned kBufSize = 1 << 15;
499   Buffer.AllocAtLeast(kBufSize);
500 
501   do
502   {
503     if (OpenCallback)
504     {
505       RINOK(Progress(item, pos))
506     }
507 
508     unsigned size = kBufSize;
509     if (size > packSize)
510       size = (unsigned)packSize;
511     size_t processedSize = size;
512     const HRESULT res = ReadStream(SeqStream, Buffer, &processedSize);
513     if (processedSize != size)
514     {
515       error = k_ErrorType_UnexpectedEnd;
516       return res;
517     }
518     if (!tb.IsNonZeroTail)
519     {
520       if (IsBufNonZero(Buffer, size))
521         tb.IsNonZeroTail = true;
522     }
523     packSize -= size;
524     pos += size;
525   }
526   while (packSize != 0);
527   return S_OK;
528 }
529 
530 
531 
532 struct CPaxInfo: public CPaxTimes
533 {
534   bool DoubleTagError;
535   bool TagParsingError;
536   bool UnknownLines_Overflow;
537   bool Size_Defined;
538   bool UID_Defined;
539   bool GID_Defined;
540   bool Path_Defined;
541   bool Link_Defined;
542   bool User_Defined;
543   bool Group_Defined;
544   bool SCHILY_fflags_Defined;
545 
546   UInt64 Size;
547   UInt32 UID;
548   UInt32 GID;
549 
550   AString Path;
551   AString Link;
552   AString User;
553   AString Group;
554   AString UnknownLines;
555   AString SCHILY_fflags;
556 
ParseIDNArchive::NTar::CPaxInfo557   bool ParseID(const AString &val, bool &defined, UInt32 &res)
558   {
559     if (defined)
560       DoubleTagError = true;
561     if (val.IsEmpty())
562       return false;
563     const char *end2;
564     res = ConvertStringToUInt32(val.Ptr(), &end2);
565     if (*end2 != 0)
566       return false;
567     defined = true;
568     return true;
569   }
570 
571   bool ParsePax(const CTempBuffer &tb, bool isFile);
572 };
573 
574 
ParsePaxTime(const AString & src,CPaxTime & pt,bool & doubleTagError)575 static bool ParsePaxTime(const AString &src, CPaxTime &pt, bool &doubleTagError)
576 {
577   if (pt.IsDefined())
578     doubleTagError = true;
579   pt.Clear();
580   const char *s = src.Ptr();
581   bool isNegative = false;
582   if (*s == '-')
583   {
584     isNegative = true;
585     s++;
586   }
587   const char *end;
588   {
589     UInt64 sec = ConvertStringToUInt64(s, &end);
590     if (s == end)
591       return false;
592     if (sec >= ((UInt64)1 << 63))
593       return false;
594     if (isNegative)
595       sec = (UInt64)-(Int64)sec;
596     pt.Sec = (Int64)sec;
597   }
598   if (*end == 0)
599   {
600     pt.Ns = 0;
601     pt.NumDigits = 0;
602     return true;
603   }
604   if (*end != '.')
605     return false;
606   s = end + 1;
607 
608   UInt32 ns = 0;
609   unsigned i;
610   const unsigned kNsDigits = 9;
611   for (i = 0;; i++)
612   {
613     const char c = s[i];
614     if (c == 0)
615       break;
616     if (c < '0' || c > '9')
617       return false;
618     // we ignore digits after 9 digits as GNU TAR
619     if (i < kNsDigits)
620     {
621       ns *= 10;
622       ns += (unsigned)(c - '0');
623     }
624   }
625   pt.NumDigits = (int)(i < kNsDigits ? i : kNsDigits);
626   while (i < kNsDigits)
627   {
628     ns *= 10;
629     i++;
630   }
631   if (isNegative && ns != 0)
632   {
633     pt.Sec--;
634     ns = (UInt32)1000 * 1000 * 1000 - ns;
635   }
636   pt.Ns = ns;
637   return true;
638 }
639 
640 
ParsePax(const CTempBuffer & tb,bool isFile)641 bool CPaxInfo::ParsePax(const CTempBuffer &tb, bool isFile)
642 {
643   DoubleTagError = false;
644   TagParsingError = false;
645   UnknownLines_Overflow = false;
646   Size_Defined = false;
647   UID_Defined = false;
648   GID_Defined = false;
649   Path_Defined = false;
650   Link_Defined = false;
651   User_Defined = false;
652   Group_Defined = false;
653   SCHILY_fflags_Defined = false;
654 
655   // CPaxTimes::Clear();
656 
657   const char *s = (const char *)(const void *)(const Byte *)tb.Buffer;
658   size_t rem = tb.StringSize;
659 
660   Clear();
661 
662   AString name, val;
663 
664   while (rem != 0)
665   {
666     unsigned i;
667     for (i = 0;; i++)
668     {
669       if (i > 24 || i >= rem) // we use limitation for size of (size) field
670         return false;
671       if (s[i] == ' ')
672         break;
673     }
674     if (i == 0)
675       return false;
676     const char *end;
677     const UInt32 size = ConvertStringToUInt32(s, &end);
678     const unsigned offset = (unsigned)(end - s) + 1;
679     if (size > rem
680         || size <= offset + 1
681         || offset != i + 1
682         || s[size - 1] != '\n')
683       return false;
684 
685     for (i = offset; i < size; i++)
686       if (s[i] == 0)
687         return false;
688 
689     for (i = offset; i < size - 1; i++)
690       if (s[i] == '=')
691         break;
692     if (i == size - 1)
693       return false;
694 
695     name.SetFrom(s + offset, i - offset);
696     val.SetFrom(s + i + 1, (unsigned)(size - 1 - (i + 1)));
697 
698     bool parsed = false;
699     if (isFile)
700     {
701       bool isDetectedName = true;
702       // only lower case (name) is supported
703       if (name.IsEqualTo("path"))
704       {
705         if (Path_Defined)
706           DoubleTagError = true;
707         Path = val;
708         Path_Defined = true;
709         parsed = true;
710       }
711       else if (name.IsEqualTo("linkpath"))
712       {
713         if (Link_Defined)
714           DoubleTagError = true;
715         Link = val;
716         Link_Defined = true;
717         parsed = true;
718       }
719       else if (name.IsEqualTo("uname"))
720       {
721         if (User_Defined)
722           DoubleTagError = true;
723         User = val;
724         User_Defined = true;
725         parsed = true;
726       }
727       else if (name.IsEqualTo("gname"))
728       {
729         if (Group_Defined)
730           DoubleTagError = true;
731         Group = val;
732         Group_Defined = true;
733         parsed = true;
734       }
735       else if (name.IsEqualTo("uid"))
736       {
737         parsed = ParseID(val, UID_Defined, UID);
738       }
739       else if (name.IsEqualTo("gid"))
740       {
741         parsed = ParseID(val, GID_Defined, GID);
742       }
743       else if (name.IsEqualTo("size"))
744       {
745         if (Size_Defined)
746           DoubleTagError = true;
747         Size_Defined = false;
748         if (!val.IsEmpty())
749         {
750           const char *end2;
751           Size = ConvertStringToUInt64(val.Ptr(), &end2);
752           if (*end2 == 0)
753           {
754             Size_Defined = true;
755             parsed = true;
756           }
757         }
758       }
759       else if (name.IsEqualTo("mtime"))
760         { parsed = ParsePaxTime(val, MTime, DoubleTagError); }
761       else if (name.IsEqualTo("atime"))
762         { parsed = ParsePaxTime(val, ATime, DoubleTagError); }
763       else if (name.IsEqualTo("ctime"))
764         { parsed = ParsePaxTime(val, CTime, DoubleTagError); }
765       else if (name.IsEqualTo("SCHILY.fflags"))
766       {
767         if (SCHILY_fflags_Defined)
768           DoubleTagError = true;
769         SCHILY_fflags = val;
770         SCHILY_fflags_Defined = true;
771         parsed = true;
772       }
773       else
774         isDetectedName = false;
775       if (isDetectedName && !parsed)
776         TagParsingError = true;
777     }
778     if (!parsed)
779     {
780       if (!UnknownLines_Overflow)
781       {
782         const unsigned addSize = size - offset;
783         if (UnknownLines.Len() + addSize < (1 << 16))
784           UnknownLines.AddFrom(s + offset, addSize);
785         else
786           UnknownLines_Overflow = true;
787       }
788     }
789 
790     s += size;
791     rem -= size;
792   }
793   return true;
794 }
795 
796 
ReadItem2(CItemEx & item)797 HRESULT CArchive::ReadItem2(CItemEx &item)
798 {
799   // CItem
800 
801   item.SparseBlocks.Clear();
802   item.PaxTimes.Clear();
803 
804   // CItemEx
805 
806   item.HeaderSize = 0;
807   item.Num_Pax_Records = 0;
808 
809   item.LongName_WasUsed = false;
810   item.LongName_WasUsed_2 = false;
811 
812   item.LongLink_WasUsed = false;
813   item.LongLink_WasUsed_2 = false;
814 
815   item.HeaderError = false;
816   item.IsSignedChecksum = false;
817   item.Prefix_WasUsed = false;
818 
819   item.Pax_Error = false;
820   item.Pax_Overflow = false;
821   item.pax_path_WasUsed = false;
822   item.pax_link_WasUsed = false;
823   item.pax_size_WasUsed = false;
824 
825   item.PaxExtra.Clear();
826   item.SCHILY_fflags.Empty();
827 
828   item.EncodingCharacts.Clear();
829 
830   // CArchive temp variable
831 
832   NameBuf.Init();
833   LinkBuf.Init();
834   PaxBuf.Init();
835   PaxBuf_global.Init();
836 
837   UInt64 numExtraRecords = 0;
838 
839   for (;;)
840   {
841     if (OpenCallback)
842     {
843       RINOK(Progress(item, 0))
844     }
845 
846     RINOK(GetNextItemReal(item))
847 
848     // NumRecords++;
849 
850     if (!filled)
851     {
852       if (error == k_ErrorType_OK)
853       if (numExtraRecords != 0
854           || item.LongName_WasUsed
855           || item.LongLink_WasUsed
856           || item.Num_Pax_Records != 0)
857         error = k_ErrorType_Corrupted;
858       return S_OK;
859     }
860     if (error != k_ErrorType_OK)
861       return S_OK;
862 
863     numExtraRecords++;
864 
865     const char lf = item.LinkFlag;
866     if (lf == NFileHeader::NLinkFlag::kGnu_LongName ||
867         lf == NFileHeader::NLinkFlag::kGnu_LongLink)
868     {
869       // GNU tar ignores item.Name after LinkFlag test
870       // 22.00 : now we also ignore item.Name here
871       /*
872       if (item.Name != NFileHeader::kLongLink &&
873           item.Name != NFileHeader::kLongLink2)
874       {
875         break;
876         // return S_OK;
877       }
878       */
879 
880       CTempBuffer *tb =
881           lf == NFileHeader::NLinkFlag::kGnu_LongName ?
882           &NameBuf :
883           &LinkBuf;
884 
885       /*
886       if (item.PackSize > (1 << 29))
887       {
888         // break;
889         return S_OK;
890       }
891       */
892 
893       const unsigned kLongNameSizeMax = (unsigned)1 << 14;
894       RINOK(ReadDataToBuffer(item, *tb, kLongNameSizeMax))
895       if (error != k_ErrorType_OK)
896         return S_OK;
897 
898       if (lf == NFileHeader::NLinkFlag::kGnu_LongName)
899       {
900         item.LongName_WasUsed_2 =
901         item.LongName_WasUsed;
902         item.LongName_WasUsed = true;
903       }
904       else
905       {
906         item.LongLink_WasUsed_2 =
907         item.LongLink_WasUsed;
908         item.LongLink_WasUsed = true;
909       }
910 
911       if (!tb->StringSize_IsConfirmed)
912         tb->StringSize = 0;
913       item.HeaderSize += item.Get_PackSize_Aligned();
914       if (tb->StringSize == 0 ||
915           tb->StringSize + 1 != item.PackSize)
916         item.HeaderError = true;
917       if (tb->IsNonZeroTail)
918         item.HeaderError = true;
919       continue;
920     }
921 
922     if (lf == NFileHeader::NLinkFlag::kGlobal ||
923         lf == NFileHeader::NLinkFlag::kPax ||
924         lf == NFileHeader::NLinkFlag::kPax_2)
925     {
926       // GNU tar ignores item.Name after LinkFlag test
927       // 22.00 : now we also ignore item.Name here
928       /*
929       if (item.PackSize > (UInt32)1 << 26)
930       {
931         break; // we don't want big PaxBuf files
932         // return S_OK;
933       }
934       */
935       const unsigned kParsingPaxSizeMax = (unsigned)1 << 26;
936 
937       const bool isStartHeader = (item.HeaderSize == NFileHeader::kRecordSize);
938 
939       CTempBuffer *tb = (lf == NFileHeader::NLinkFlag::kGlobal ? &PaxBuf_global : &PaxBuf);
940 
941       RINOK(ReadDataToBuffer(item, *tb, kParsingPaxSizeMax))
942       if (error != k_ErrorType_OK)
943         return S_OK;
944 
945       item.HeaderSize += item.Get_PackSize_Aligned();
946 
947       if (tb->StringSize != item.PackSize
948           || tb->StringSize == 0
949           || tb->IsNonZeroTail)
950         item.Pax_Error = true;
951 
952       item.Num_Pax_Records++;
953       if (lf != NFileHeader::NLinkFlag::kGlobal)
954       {
955         item.PaxExtra.RecordPath = item.Name;
956         continue;
957       }
958       // break; // for debug
959       {
960         if (PaxGlobal_Defined)
961           _is_PaxGlobal_Error = true;
962         CPaxInfo paxInfo;
963         if (paxInfo.ParsePax(PaxBuf_global, false))
964         {
965           PaxGlobal.RawLines = paxInfo.UnknownLines;
966           PaxGlobal.RecordPath = item.Name;
967           PaxGlobal_Defined = true;
968         }
969         else
970           _is_PaxGlobal_Error = true;
971 
972         if (isStartHeader
973             && item.Num_Pax_Records == 1
974             && numExtraRecords == 1)
975         {
976           // we skip global pax header info after parsing
977           item.HeaderPos += item.HeaderSize;
978           item.HeaderSize = 0;
979           item.Num_Pax_Records = 0;
980           numExtraRecords = 0;
981         }
982         else
983           _is_PaxGlobal_Error = true;
984       }
985       continue;
986     }
987 
988     /*
989     if (lf == NFileHeader::NLinkFlag::kDumpDir ||
990         lf == NFileHeader::NLinkFlag::kSparse)
991     {
992       // GNU Extensions to the Archive Format
993       break;
994     }
995     if (lf > '7' || (lf < '0' && lf != 0))
996     {
997       break;
998       // return S_OK;
999     }
1000     */
1001     break;
1002   }
1003 
1004   // we still use name from main header, if long_name is bad
1005   if (item.LongName_WasUsed && NameBuf.StringSize != 0)
1006   {
1007     NameBuf.CopyToString(item.Name);
1008     // item.Name_CouldBeReduced = false;
1009   }
1010 
1011   if (item.LongLink_WasUsed)
1012   {
1013     // we use empty link, if long_link is bad
1014     LinkBuf.CopyToString(item.LinkName);
1015     // item.LinkName_CouldBeReduced = false;
1016   }
1017 
1018   error = k_ErrorType_OK;
1019 
1020   if (PaxBuf.StringSize != 0)
1021   {
1022     CPaxInfo paxInfo;
1023     if (!paxInfo.ParsePax(PaxBuf, true))
1024       item.Pax_Error = true;
1025     else
1026     {
1027       if (paxInfo.Path_Defined) //  if (!paxInfo.Path.IsEmpty())
1028       {
1029         item.Name = paxInfo.Path;
1030         item.pax_path_WasUsed = true;
1031       }
1032       if (paxInfo.Link_Defined) // (!paxInfo.Link.IsEmpty())
1033       {
1034         item.LinkName = paxInfo.Link;
1035         item.pax_link_WasUsed = true;
1036       }
1037       if (paxInfo.User_Defined)
1038       {
1039         item.User = paxInfo.User;
1040         // item.pax_uname_WasUsed = true;
1041       }
1042       if (paxInfo.Group_Defined)
1043       {
1044         item.Group = paxInfo.Group;
1045         // item.pax_gname_WasUsed = true;
1046       }
1047       if (paxInfo.SCHILY_fflags_Defined)
1048       {
1049         item.SCHILY_fflags = paxInfo.SCHILY_fflags;
1050         // item.SCHILY_fflags_WasUsed = true;
1051       }
1052       if (paxInfo.UID_Defined)
1053       {
1054         item.UID = (UInt32)paxInfo.UID;
1055       }
1056       if (paxInfo.GID_Defined)
1057       {
1058         item.GID = (UInt32)paxInfo.GID;
1059       }
1060 
1061       if (paxInfo.Size_Defined)
1062       {
1063         const UInt64 piSize = paxInfo.Size;
1064         // GNU TAR ignores (item.Size) in that case
1065         if (item.Size != 0 && item.Size != piSize)
1066           item.Pax_Error = true;
1067         item.Size = piSize;
1068         item.PackSize = piSize;
1069         item.pax_size_WasUsed = true;
1070       }
1071 
1072       item.PaxTimes = paxInfo;
1073       item.PaxExtra.RawLines = paxInfo.UnknownLines;
1074       if (paxInfo.UnknownLines_Overflow)
1075         item.Pax_Overflow = true;
1076       if (paxInfo.TagParsingError)
1077         item.Pax_Error = true;
1078       if (paxInfo.DoubleTagError)
1079         item.Pax_Error = true;
1080     }
1081   }
1082 
1083   return S_OK;
1084 }
1085 
1086 
1087 
ReadItem(CItemEx & item)1088 HRESULT CArchive::ReadItem(CItemEx &item)
1089 {
1090   item.HeaderPos = _phySize;
1091 
1092   const HRESULT res = ReadItem2(item);
1093 
1094   /*
1095   if (error == k_ErrorType_Warning)
1096     _is_Warning = true;
1097   else
1098   */
1099 
1100   if (error != k_ErrorType_OK)
1101     _error = error;
1102 
1103   RINOK(res)
1104 
1105   if (filled)
1106   {
1107     if (item.IsMagic_GNU())
1108       _are_Gnu = true;
1109     else if (item.IsMagic_Posix_ustar_00())
1110       _are_Posix = true;
1111 
1112     if (item.Num_Pax_Records != 0)
1113       _are_Pax = true;
1114 
1115     if (item.PaxTimes.MTime.IsDefined())  _are_mtime = true;
1116     if (item.PaxTimes.ATime.IsDefined())  _are_atime = true;
1117     if (item.PaxTimes.CTime.IsDefined())  _are_ctime = true;
1118     if (!item.SCHILY_fflags.IsEmpty())  _are_SCHILY_fflags = true;
1119 
1120     if (item.pax_path_WasUsed)
1121       _are_pax_path = true;
1122     if (item.pax_link_WasUsed)
1123       _are_pax_link = true;
1124     if (item.LongName_WasUsed)
1125       _are_LongName = true;
1126     if (item.LongLink_WasUsed)
1127       _are_LongLink = true;
1128     if (item.Prefix_WasUsed)
1129       _pathPrefix_WasUsed = true;
1130     /*
1131     if (item.IsSparse())
1132       _isSparse = true;
1133     */
1134     if (item.Is_PaxExtendedHeader())
1135       _are_Pax_Items = true;
1136     if (item.IsThereWarning()
1137         || item.HeaderError
1138         || item.Pax_Error)
1139       _is_Warning = true;
1140   }
1141 
1142   const UInt64 headerEnd = item.HeaderPos + item.HeaderSize;
1143   // _headersSize += headerEnd - _phySize;
1144   // we don't count skipped records
1145   _headersSize += item.HeaderSize;
1146   _phySize = headerEnd;
1147   return S_OK;
1148 }
1149 
1150 }}
1151