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