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