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 namespace NArchive {
16 namespace NTar {
17 
MyStrNCpy(char * dest,const char * src,unsigned size)18 static void MyStrNCpy(char *dest, const char *src, unsigned size)
19 {
20   for (unsigned i = 0; i < size; i++)
21   {
22     char c = src[i];
23     dest[i] = c;
24     if (c == 0)
25       break;
26   }
27 }
28 
OctalToNumber(const char * srcString,unsigned size,UInt64 & res,bool allowEmpty=false)29 static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res, bool allowEmpty = false)
30 {
31   res = 0;
32   char sz[32];
33   MyStrNCpy(sz, srcString, size);
34   sz[size] = 0;
35   const char *end;
36   unsigned i;
37   for (i = 0; sz[i] == ' '; i++);
38   if (sz[i] == 0)
39     return allowEmpty;
40   res = ConvertOctStringToUInt64(sz + i, &end);
41   return (*end == ' ' || *end == 0);
42 }
43 
OctalToNumber32(const char * srcString,unsigned size,UInt32 & res,bool allowEmpty=false)44 static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, bool allowEmpty = false)
45 {
46   UInt64 res64;
47   if (!OctalToNumber(srcString, size, res64, allowEmpty))
48     return false;
49   res = (UInt32)res64;
50   return (res64 <= 0xFFFFFFFF);
51 }
52 
53 #define RIF(x) { if (!(x)) return S_OK; }
54 
55 /*
56 static bool IsEmptyData(const char *buf, size_t size)
57 {
58   for (unsigned i = 0; i < size; i++)
59     if (buf[i] != 0)
60       return false;
61   return true;
62 }
63 */
64 
IsRecordLast(const char * buf)65 static bool IsRecordLast(const char *buf)
66 {
67   for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
68     if (buf[i] != 0)
69       return false;
70   return true;
71 }
72 
ReadString(const char * s,unsigned size,AString & result)73 static void ReadString(const char *s, unsigned size, AString &result)
74 {
75   char temp[NFileHeader::kRecordSize + 1];
76   MyStrNCpy(temp, s, size);
77   temp[size] = '\0';
78   result = temp;
79 }
80 
ParseInt64(const char * p,Int64 & val)81 static bool ParseInt64(const char *p, Int64 &val)
82 {
83   UInt32 h = GetBe32(p);
84   val = GetBe64(p + 4);
85   if (h == (UInt32)1 << 31)
86     return ((val >> 63) & 1) == 0;
87   if (h == (UInt32)(Int32)-1)
88     return ((val >> 63) & 1) != 0;
89   UInt64 uv;
90   bool res = OctalToNumber(p, 12, uv);
91   val = uv;
92   return res;
93 }
94 
ParseInt64_MTime(const char * p,Int64 & val)95 static bool ParseInt64_MTime(const char *p, Int64 &val)
96 {
97   // rare case tar contains spaces instead of MTime
98   for (unsigned i = 0; i < 12; i++)
99     if (p[i] != ' ')
100       return ParseInt64(p, val);
101   val = 0;
102   return true;
103 }
104 
ParseSize(const char * p,UInt64 & val)105 static bool ParseSize(const char *p, UInt64 &val)
106 {
107   if (GetBe32(p) == (UInt32)1 << 31)
108   {
109     // GNU extension
110     val = GetBe64(p + 4);
111     return ((val >> 63) & 1) == 0;
112   }
113   return OctalToNumber(p, 12, val);
114 }
115 
116 #define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
117 
IsArc_Tar(const Byte * p2,size_t size)118 API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
119 {
120   if (size < NFileHeader::kRecordSize)
121     return k_IsArc_Res_NEED_MORE;
122 
123   const char *p = (const char *)p2;
124   p += NFileHeader::kNameSize;
125 
126   UInt32 mode;
127   // we allow empty Mode value for LongName prefix items
128   CHECK(OctalToNumber32(p, 8, mode, true)); p += 8;
129 
130   // if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0;
131   p += 8;
132   // if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0;
133   p += 8;
134 
135   UInt64 packSize;
136   Int64 time;
137   UInt32 checkSum;
138   CHECK(ParseSize(p, packSize)); p += 12;
139   CHECK(ParseInt64_MTime(p, time)); p += 12;
140   CHECK(OctalToNumber32(p, 8, checkSum));
141   return k_IsArc_Res_YES;
142 }
143 
GetNextItemReal(ISequentialInStream * stream,bool & filled,CItemEx & item,EErrorType & error)144 static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
145 {
146   char buf[NFileHeader::kRecordSize];
147   char *p = buf;
148 
149   error = k_ErrorType_OK;
150   filled = false;
151 
152   bool thereAreEmptyRecords = false;
153   for (;;)
154   {
155     size_t processedSize = NFileHeader::kRecordSize;
156     RINOK(ReadStream(stream, buf, &processedSize));
157     if (processedSize == 0)
158     {
159       if (!thereAreEmptyRecords)
160         error = k_ErrorType_UnexpectedEnd; // "There are no trailing zero-filled records";
161       return S_OK;
162     }
163     if (processedSize != NFileHeader::kRecordSize)
164     {
165       if (!thereAreEmptyRecords)
166         error = k_ErrorType_UnexpectedEnd; // error = "There is no correct record at the end of archive";
167       else
168       {
169         /*
170         if (IsEmptyData(buf, processedSize))
171           error = k_ErrorType_UnexpectedEnd;
172         else
173         {
174           // extraReadSize = processedSize;
175           // error = k_ErrorType_Corrupted; // some data after the end tail zeros
176         }
177         */
178       }
179 
180       return S_OK;
181     }
182     if (!IsRecordLast(buf))
183       break;
184     item.HeaderSize += NFileHeader::kRecordSize;
185     thereAreEmptyRecords = true;
186   }
187   if (thereAreEmptyRecords)
188   {
189     // error = "There are data after end of archive";
190     return S_OK;
191   }
192 
193   error = k_ErrorType_Corrupted;
194   ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize;
195   item.NameCouldBeReduced =
196       (item.Name.Len() == NFileHeader::kNameSize ||
197        item.Name.Len() == NFileHeader::kNameSize - 1);
198 
199   // we allow empty Mode value for LongName prefix items
200   RIF(OctalToNumber32(p, 8, item.Mode, true)); p += 8;
201 
202   if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0; p += 8;
203   if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0; p += 8;
204 
205   RIF(ParseSize(p, item.PackSize));
206   item.Size = item.PackSize;
207   p += 12;
208   RIF(ParseInt64_MTime(p, item.MTime)); p += 12;
209 
210   UInt32 checkSum;
211   RIF(OctalToNumber32(p, 8, checkSum));
212   memset(p, ' ', 8); p += 8;
213 
214   item.LinkFlag = *p++;
215 
216   ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
217   item.LinkNameCouldBeReduced =
218       (item.LinkName.Len() == NFileHeader::kNameSize ||
219        item.LinkName.Len() == NFileHeader::kNameSize - 1);
220 
221   memcpy(item.Magic, p, 8); p += 8;
222 
223   ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
224   ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
225 
226   item.DeviceMajorDefined = (p[0] != 0); if (item.DeviceMajorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMajor)); } p += 8;
227   item.DeviceMinorDefined = (p[0] != 0); if (item.DeviceMinorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMinor)); } p += 8;
228 
229   if (p[0] != 0)
230   {
231     AString prefix;
232     ReadString(p, NFileHeader::kPrefixSize, prefix);
233     if (!prefix.IsEmpty()
234         && item.IsUstarMagic()
235         && (item.LinkFlag != 'L' /* || prefix != "00000000000" */ ))
236       item.Name = prefix + '/' + item.Name;
237   }
238 
239   p += NFileHeader::kPrefixSize;
240 
241   if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
242   {
243     item.PackSize = 0;
244     item.Size = 0;
245   }
246   /*
247     TAR standard requires sum of unsigned byte values.
248     But some TAR programs use sum of signed byte values.
249     So we check both values.
250   */
251   UInt32 checkSumReal = 0;
252   Int32 checkSumReal_Signed = 0;
253   for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
254   {
255     char c = buf[i];
256     checkSumReal_Signed += (signed char)c;
257     checkSumReal += (Byte)buf[i];
258   }
259 
260   if (checkSumReal != checkSum)
261   {
262     if ((UInt32)checkSumReal_Signed != checkSum)
263       return S_OK;
264   }
265 
266   item.HeaderSize += NFileHeader::kRecordSize;
267 
268   if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse)
269   {
270     Byte isExtended = buf[482];
271     if (isExtended != 0 && isExtended != 1)
272       return S_OK;
273     RIF(ParseSize(buf + 483, item.Size));
274     UInt64 min = 0;
275     for (unsigned i = 0; i < 4; i++)
276     {
277       p = buf + 386 + 24 * i;
278       if (GetBe32(p) == 0)
279       {
280         if (isExtended != 0)
281           return S_OK;
282         break;
283       }
284       CSparseBlock sb;
285       RIF(ParseSize(p, sb.Offset));
286       RIF(ParseSize(p + 12, sb.Size));
287       item.SparseBlocks.Add(sb);
288       if (sb.Offset < min || sb.Offset > item.Size)
289         return S_OK;
290       if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
291         return S_OK;
292       min = sb.Offset + sb.Size;
293       if (min < sb.Offset)
294         return S_OK;
295     }
296     if (min > item.Size)
297       return S_OK;
298 
299     while (isExtended != 0)
300     {
301       size_t processedSize = NFileHeader::kRecordSize;
302       RINOK(ReadStream(stream, buf, &processedSize));
303       if (processedSize != NFileHeader::kRecordSize)
304       {
305         error = k_ErrorType_UnexpectedEnd;
306         return S_OK;
307       }
308 
309       item.HeaderSize += NFileHeader::kRecordSize;
310       isExtended = buf[21 * 24];
311       if (isExtended != 0 && isExtended != 1)
312         return S_OK;
313       for (unsigned i = 0; i < 21; i++)
314       {
315         p = buf + 24 * i;
316         if (GetBe32(p) == 0)
317         {
318           if (isExtended != 0)
319             return S_OK;
320           break;
321         }
322         CSparseBlock sb;
323         RIF(ParseSize(p, sb.Offset));
324         RIF(ParseSize(p + 12, sb.Size));
325         item.SparseBlocks.Add(sb);
326         if (sb.Offset < min || sb.Offset > item.Size)
327           return S_OK;
328         if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
329           return S_OK;
330         min = sb.Offset + sb.Size;
331         if (min < sb.Offset)
332           return S_OK;
333       }
334     }
335     if (min > item.Size)
336       return S_OK;
337   }
338 
339   filled = true;
340   error = k_ErrorType_OK;
341   return S_OK;
342 }
343 
344 
ReadDataToString(ISequentialInStream * stream,CItemEx & item,AString & s,EErrorType & error)345 static HRESULT ReadDataToString(ISequentialInStream *stream, CItemEx &item, AString &s, EErrorType &error)
346 {
347   const unsigned packSize = (unsigned)item.GetPackSizeAligned();
348   size_t processedSize = packSize;
349   HRESULT res = ReadStream(stream, s.GetBuf(packSize), &processedSize);
350   item.HeaderSize += (unsigned)processedSize;
351   s.ReleaseBuf_CalcLen((unsigned)item.PackSize);
352   RINOK(res);
353   if (processedSize != packSize)
354     error = k_ErrorType_UnexpectedEnd;
355   return S_OK;
356 }
357 
ParsePaxLongName(const AString & src,AString & dest)358 static bool ParsePaxLongName(const AString &src, AString &dest)
359 {
360   dest.Empty();
361   for (unsigned pos = 0;;)
362   {
363     if (pos >= src.Len())
364       return false;
365     const char *start = src.Ptr(pos);
366     const char *end;
367     const UInt32 lineLen = ConvertStringToUInt32(start, &end);
368     if (end == start)
369       return false;
370     if (*end != ' ')
371       return false;
372     if (lineLen > src.Len() - pos)
373       return false;
374     unsigned offset = (unsigned)(end - start) + 1;
375     if (lineLen < offset)
376       return false;
377     if (IsString1PrefixedByString2(src.Ptr(pos + offset), "path="))
378     {
379       offset += 5; // "path="
380       dest = src.Mid(pos + offset, lineLen - offset);
381       if (dest.IsEmpty())
382         return false;
383       if (dest.Back() != '\n')
384         return false;
385       dest.DeleteBack();
386       return true;
387     }
388     pos += lineLen;
389   }
390 }
391 
ReadItem(ISequentialInStream * stream,bool & filled,CItemEx & item,EErrorType & error)392 HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
393 {
394   item.HeaderSize = 0;
395 
396   bool flagL = false;
397   bool flagK = false;
398   AString nameL;
399   AString nameK;
400   AString pax;
401 
402   for (;;)
403   {
404     RINOK(GetNextItemReal(stream, filled, item, error));
405     if (!filled)
406     {
407       if (error == k_ErrorType_OK && (flagL || flagK))
408         error = k_ErrorType_Corrupted;
409       return S_OK;
410     }
411 
412     if (error != k_ErrorType_OK)
413       return S_OK;
414 
415     if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName || // file contains a long name
416         item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongLink)   // file contains a long linkname
417     {
418       AString *name;
419       if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName)
420         { if (flagL) return S_OK; flagL = true; name = &nameL; }
421       else
422         { if (flagK) return S_OK; flagK = true; name = &nameK; }
423 
424       if (item.Name != NFileHeader::kLongLink &&
425           item.Name != NFileHeader::kLongLink2)
426         return S_OK;
427       if (item.PackSize > (1 << 14))
428         return S_OK;
429 
430       RINOK(ReadDataToString(stream, item, *name, error));
431       if (error != k_ErrorType_OK)
432         return S_OK;
433 
434       continue;
435     }
436 
437     switch (item.LinkFlag)
438     {
439       case 'g':
440       case 'x':
441       case 'X':
442       {
443         // pax Extended Header
444         if (item.Name.IsPrefixedBy("PaxHeader/"))
445         {
446           RINOK(ReadDataToString(stream, item, pax, error));
447           if (error != k_ErrorType_OK)
448             return S_OK;
449           continue;
450         }
451 
452         break;
453       }
454       case NFileHeader::NLinkFlag::kDumpDir:
455       {
456         break;
457         // GNU Extensions to the Archive Format
458       }
459       case NFileHeader::NLinkFlag::kSparse:
460       {
461         break;
462         // GNU Extensions to the Archive Format
463       }
464       default:
465         if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
466           return S_OK;
467     }
468 
469     if (flagL)
470     {
471       item.Name = nameL;
472       item.NameCouldBeReduced = false;
473     }
474 
475     if (flagK)
476     {
477       item.LinkName = nameK;
478       item.LinkNameCouldBeReduced = false;
479     }
480 
481     error = k_ErrorType_OK;
482 
483     if (!pax.IsEmpty())
484     {
485       AString name;
486       if (ParsePaxLongName(pax, name))
487         item.Name = name;
488       else
489         error = k_ErrorType_Warning;
490     }
491 
492     return S_OK;
493   }
494 }
495 
496 }}
497