aboutsummaryrefslogtreecommitdiff
path: root/CPP/7zip/Archive/Tar
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/Tar')
-rw-r--r--CPP/7zip/Archive/Tar/StdAfx.h8
-rw-r--r--CPP/7zip/Archive/Tar/TarHandler.cpp777
-rw-r--r--CPP/7zip/Archive/Tar/TarHandler.h81
-rw-r--r--CPP/7zip/Archive/Tar/TarHandlerOut.cpp183
-rw-r--r--CPP/7zip/Archive/Tar/TarHeader.cpp26
-rw-r--r--CPP/7zip/Archive/Tar/TarHeader.h86
-rw-r--r--CPP/7zip/Archive/Tar/TarIn.cpp517
-rw-r--r--CPP/7zip/Archive/Tar/TarIn.h27
-rw-r--r--CPP/7zip/Archive/Tar/TarItem.h182
-rw-r--r--CPP/7zip/Archive/Tar/TarOut.cpp279
-rw-r--r--CPP/7zip/Archive/Tar/TarOut.h36
-rw-r--r--CPP/7zip/Archive/Tar/TarRegister.cpp23
-rw-r--r--CPP/7zip/Archive/Tar/TarUpdate.cpp259
-rw-r--r--CPP/7zip/Archive/Tar/TarUpdate.h41
14 files changed, 2525 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Tar/StdAfx.h b/CPP/7zip/Archive/Tar/StdAfx.h
new file mode 100644
index 0000000..2854ff3
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/StdAfx.h
@@ -0,0 +1,8 @@
1// StdAfx.h
2
3#ifndef __STDAFX_H
4#define __STDAFX_H
5
6#include "../../../Common/Common.h"
7
8#endif
diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp
new file mode 100644
index 0000000..2f23dd8
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarHandler.cpp
@@ -0,0 +1,777 @@
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
22using namespace NWindows;
23
24namespace NArchive {
25namespace 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
32static const Byte kProps[] =
33{
34 kpidPath,
35 kpidIsDir,
36 kpidSize,
37 kpidPackSize,
38 kpidMTime,
39 kpidPosixAttrib,
40 kpidUser,
41 kpidGroup,
42 kpidSymLink,
43 kpidHardLink,
44 kpidCharacts
45 // kpidLinkType
46};
47
48static const Byte kArcProps[] =
49{
50 kpidHeadersSize,
51 kpidCodePage,
52 kpidCharacts
53};
54
55IMP_IInArchive_Props
56IMP_IInArchive_ArcProps
57
58STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
59{
60 NCOM::CPropVariant prop;
61 switch (propID)
62 {
63 case kpidPhySize: if (_phySizeDefined) prop = _phySize; break;
64 case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break;
65 case kpidErrorFlags:
66 {
67 UInt32 flags = 0;
68 if (!_isArc)
69 flags |= kpv_ErrorFlags_IsNotArc;
70 else switch (_error)
71 {
72 case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break;
73 case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break;
74 // case k_ErrorType_OK: break;
75 // case k_ErrorType_Warning: break;
76 default: break;
77 }
78 if (flags != 0)
79 prop = flags;
80 break;
81 }
82
83 case kpidWarningFlags:
84 {
85 if (_warning)
86 prop = kpv_ErrorFlags_HeadersError;
87 break;
88 }
89
90 case kpidCodePage:
91 {
92 char sz[16];
93 const char *name = NULL;
94 switch (_openCodePage)
95 {
96 case CP_OEMCP: name = "OEM"; break;
97 case CP_UTF8: name = "UTF-8"; break;
98 }
99 if (!name)
100 {
101 ConvertUInt32ToString(_openCodePage, sz);
102 name = sz;
103 }
104 prop = name;
105 break;
106 }
107
108 case kpidCharacts:
109 {
110 prop = _encodingCharacts.GetCharactsString();
111 break;
112 }
113 }
114 prop.Detach(value);
115 return S_OK;
116}
117
118HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item)
119{
120 item.HeaderPos = _phySize;
121 EErrorType error;
122 HRESULT res = ReadItem(stream, filled, item, error);
123 if (error == k_ErrorType_Warning)
124 _warning = true;
125 else if (error != k_ErrorType_OK)
126 _error = error;
127 RINOK(res);
128 if (filled)
129 {
130 /*
131 if (item.IsSparse())
132 _isSparse = true;
133 */
134 if (item.IsPaxExtendedHeader())
135 _thereIsPaxExtendedHeader = true;
136 if (item.IsThereWarning())
137 _warning = true;
138 }
139 _phySize += item.HeaderSize;
140 _headersSize += item.HeaderSize;
141 return S_OK;
142}
143
144
145void CEncodingCharacts::Check(const AString &s)
146{
147 IsAscii = s.IsAscii();
148 if (!IsAscii)
149 {
150 /*
151 {
152 Oem_Checked = true;
153 UString u;
154 MultiByteToUnicodeString2(u, s, CP_OEMCP);
155 Oem_Ok = (u.Find((wchar_t)0xfffd) <= 0);
156 }
157 Utf_Checked = true;
158 */
159 UtfCheck.Check_AString(s);
160 }
161}
162
163
164AString CEncodingCharacts::GetCharactsString() const
165{
166 AString s;
167 if (IsAscii)
168 {
169 s += "ASCII";
170 }
171 /*
172 if (Oem_Checked)
173 {
174 s.Add_Space_if_NotEmpty();
175 s += (Oem_Ok ? "oem-ok" : "oem-error");
176 }
177 if (Utf_Checked)
178 */
179 else
180 {
181 s.Add_Space_if_NotEmpty();
182 s += (UtfCheck.IsOK() ? "UTF8" : "UTF8-ERROR"); // "UTF8-error"
183 {
184 AString s2;
185 UtfCheck.PrintStatus(s2);
186 s.Add_Space_if_NotEmpty();
187 s += s2;
188 }
189 }
190 return s;
191}
192
193
194HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
195{
196 UInt64 endPos = 0;
197 {
198 RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
199 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
200 }
201
202 _phySizeDefined = true;
203
204 // bool utf8_OK = true;
205
206 for (;;)
207 {
208 CItemEx item;
209 bool filled;
210 RINOK(ReadItem2(stream, filled, item));
211 if (!filled)
212 break;
213
214 _isArc = true;
215
216 /*
217 if (!_forceCodePage)
218 {
219 if (utf8_OK) utf8_OK = CheckUTF8(item.Name, item.NameCouldBeReduced);
220 if (utf8_OK) utf8_OK = CheckUTF8(item.LinkName, item.LinkNameCouldBeReduced);
221 if (utf8_OK) utf8_OK = CheckUTF8(item.User);
222 if (utf8_OK) utf8_OK = CheckUTF8(item.Group);
223 }
224 */
225
226 item.EncodingCharacts.Check(item.Name);
227 _encodingCharacts.Update(item.EncodingCharacts);
228
229 _items.Add(item);
230
231 RINOK(stream->Seek((Int64)item.GetPackSizeAligned(), STREAM_SEEK_CUR, &_phySize));
232 if (_phySize > endPos)
233 {
234 _error = k_ErrorType_UnexpectedEnd;
235 break;
236 }
237 /*
238 if (_phySize == endPos)
239 {
240 _errorMessage = "There are no trailing zero-filled records";
241 break;
242 }
243 */
244 if (callback)
245 {
246 if (_items.Size() == 1)
247 {
248 RINOK(callback->SetTotal(NULL, &endPos));
249 }
250 if ((_items.Size() & 0x3FF) == 0)
251 {
252 UInt64 numFiles = _items.Size();
253 RINOK(callback->SetCompleted(&numFiles, &_phySize));
254 }
255 }
256 }
257
258 /*
259 if (!_forceCodePage)
260 {
261 if (!utf8_OK)
262 _curCodePage = k_DefaultCodePage;
263 }
264 */
265 _openCodePage = _curCodePage;
266
267 if (_items.Size() == 0)
268 {
269 if (_error != k_ErrorType_OK)
270 {
271 _isArc = false;
272 return S_FALSE;
273 }
274 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
275 if (!callback)
276 return S_FALSE;
277 callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
278 if (!openVolumeCallback)
279 return S_FALSE;
280 NCOM::CPropVariant prop;
281 if (openVolumeCallback->GetProperty(kpidName, &prop) != S_OK)
282 return S_FALSE;
283 if (prop.vt != VT_BSTR)
284 return S_FALSE;
285 unsigned len = MyStringLen(prop.bstrVal);
286 if (len < 4 || MyStringCompareNoCase(prop.bstrVal + len - 4, L".tar") != 0)
287 return S_FALSE;
288 }
289
290 _isArc = true;
291 return S_OK;
292}
293
294STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback)
295{
296 COM_TRY_BEGIN
297 {
298 Close();
299 RINOK(Open2(stream, openArchiveCallback));
300 _stream = stream;
301 }
302 return S_OK;
303 COM_TRY_END
304}
305
306STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
307{
308 Close();
309 _seqStream = stream;
310 _isArc = true;
311 return S_OK;
312}
313
314STDMETHODIMP CHandler::Close()
315{
316 _isArc = false;
317 _warning = false;
318 _error = k_ErrorType_OK;
319
320 _phySizeDefined = false;
321 _phySize = 0;
322 _headersSize = 0;
323 _curIndex = 0;
324 _latestIsRead = false;
325 // _isSparse = false;
326 _thereIsPaxExtendedHeader = false;
327 _encodingCharacts.Clear();
328 _items.Clear();
329 _seqStream.Release();
330 _stream.Release();
331 return S_OK;
332}
333
334STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
335{
336 *numItems = (_stream ? _items.Size() : (UInt32)(Int32)-1);
337 return S_OK;
338}
339
340CHandler::CHandler()
341{
342 copyCoderSpec = new NCompress::CCopyCoder();
343 copyCoder = copyCoderSpec;
344 _openCodePage = CP_UTF8;
345 Init();
346}
347
348HRESULT CHandler::SkipTo(UInt32 index)
349{
350 while (_curIndex < index || !_latestIsRead)
351 {
352 if (_latestIsRead)
353 {
354 UInt64 packSize = _latestItem.GetPackSizeAligned();
355 RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
356 _phySize += copyCoderSpec->TotalSize;
357 if (copyCoderSpec->TotalSize != packSize)
358 {
359 _error = k_ErrorType_UnexpectedEnd;
360 return S_FALSE;
361 }
362 _latestIsRead = false;
363 _curIndex++;
364 }
365 else
366 {
367 bool filled;
368 RINOK(ReadItem2(_seqStream, filled, _latestItem));
369 if (!filled)
370 {
371 _phySizeDefined = true;
372 return E_INVALIDARG;
373 }
374 _latestIsRead = true;
375 }
376 }
377 return S_OK;
378}
379
380void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs) const
381{
382 UString dest;
383 if (_curCodePage == CP_UTF8)
384 ConvertUTF8ToUnicode(s, dest);
385 else
386 MultiByteToUnicodeString2(dest, s, _curCodePage);
387 if (toOs)
388 NItemName::ReplaceToOsSlashes_Remove_TailSlash(dest,
389 true); // useBackslashReplacement
390 prop = dest;
391}
392
393STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
394{
395 COM_TRY_BEGIN
396 NCOM::CPropVariant prop;
397
398 const CItemEx *item;
399 if (_stream)
400 item = &_items[index];
401 else
402 {
403 if (index < _curIndex)
404 return E_INVALIDARG;
405 else
406 {
407 RINOK(SkipTo(index));
408 item = &_latestItem;
409 }
410 }
411
412 switch (propID)
413 {
414 case kpidPath: TarStringToUnicode(item->Name, prop, true); break;
415 case kpidIsDir: prop = item->IsDir(); break;
416 case kpidSize: prop = item->GetUnpackSize(); break;
417 case kpidPackSize: prop = item->GetPackSizeAligned(); break;
418 case kpidMTime:
419 if (item->MTime != 0)
420 {
421 FILETIME ft;
422 if (NTime::UnixTime64ToFileTime(item->MTime, ft))
423 prop = ft;
424 }
425 break;
426 case kpidPosixAttrib: prop = item->Get_Combined_Mode(); break;
427 case kpidUser: TarStringToUnicode(item->User, prop); break;
428 case kpidGroup: TarStringToUnicode(item->Group, prop); break;
429 case kpidSymLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kSymLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break;
430 case kpidHardLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kHardLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break;
431 // case kpidLinkType: prop = (int)item->LinkFlag; break;
432 case kpidCharacts:
433 {
434 AString s = item->EncodingCharacts.GetCharactsString();
435 if (item->IsThereWarning())
436 {
437 s.Add_Space_if_NotEmpty();
438 s += "HEADER_ERROR";
439 }
440 prop = s;
441 break;
442 }
443 }
444 prop.Detach(value);
445 return S_OK;
446 COM_TRY_END
447}
448
449HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
450 Int32 testMode, IArchiveExtractCallback *extractCallback)
451{
452 COM_TRY_BEGIN
453 ISequentialInStream *stream = _seqStream;
454 bool seqMode = (_stream == NULL);
455 if (!seqMode)
456 stream = _stream;
457
458 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
459 if (allFilesMode)
460 numItems = _items.Size();
461 if (_stream && numItems == 0)
462 return S_OK;
463 UInt64 totalSize = 0;
464 UInt32 i;
465 for (i = 0; i < numItems; i++)
466 totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize();
467 extractCallback->SetTotal(totalSize);
468
469 UInt64 totalPackSize;
470 totalSize = totalPackSize = 0;
471
472 CLocalProgress *lps = new CLocalProgress;
473 CMyComPtr<ICompressProgressInfo> progress = lps;
474 lps->Init(extractCallback, false);
475
476 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
477 CMyComPtr<ISequentialInStream> inStream(streamSpec);
478 streamSpec->SetStream(stream);
479
480 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
481 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
482
483 for (i = 0; i < numItems || seqMode; i++)
484 {
485 lps->InSize = totalPackSize;
486 lps->OutSize = totalSize;
487 RINOK(lps->SetCur());
488 CMyComPtr<ISequentialOutStream> realOutStream;
489 Int32 askMode = testMode ?
490 NExtract::NAskMode::kTest :
491 NExtract::NAskMode::kExtract;
492 const UInt32 index = allFilesMode ? i : indices[i];
493 const CItemEx *item;
494 if (seqMode)
495 {
496 HRESULT res = SkipTo(index);
497 if (res == E_INVALIDARG)
498 break;
499 RINOK(res);
500 item = &_latestItem;
501 }
502 else
503 item = &_items[index];
504
505 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
506 UInt64 unpackSize = item->GetUnpackSize();
507 totalSize += unpackSize;
508 totalPackSize += item->GetPackSizeAligned();
509 if (item->IsDir())
510 {
511 RINOK(extractCallback->PrepareOperation(askMode));
512 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
513 continue;
514 }
515 bool skipMode = false;
516 if (!testMode && !realOutStream)
517 {
518 if (!seqMode)
519 {
520 /*
521 // probably we must show extracting info it callback handler instead
522 if (item->IsHardLink() ||
523 item->IsSymLink())
524 {
525 RINOK(extractCallback->PrepareOperation(askMode));
526 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
527 }
528 */
529 continue;
530 }
531 skipMode = true;
532 askMode = NExtract::NAskMode::kSkip;
533 }
534 RINOK(extractCallback->PrepareOperation(askMode));
535
536 outStreamSpec->SetStream(realOutStream);
537 realOutStream.Release();
538 outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
539
540 Int32 opRes = NExtract::NOperationResult::kOK;
541 CMyComPtr<ISequentialInStream> inStream2;
542 if (!item->IsSparse())
543 inStream2 = inStream;
544 else
545 {
546 GetStream(index, &inStream2);
547 if (!inStream2)
548 return E_FAIL;
549 }
550
551 {
552 if (item->IsSymLink())
553 {
554 RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len()));
555 }
556 else
557 {
558 if (!seqMode)
559 {
560 RINOK(_stream->Seek((Int64)item->GetDataPosition(), STREAM_SEEK_SET, NULL));
561 }
562 streamSpec->Init(item->GetPackSizeAligned());
563 RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress));
564 }
565 if (outStreamSpec->GetRem() != 0)
566 opRes = NExtract::NOperationResult::kDataError;
567 }
568 if (seqMode)
569 {
570 _latestIsRead = false;
571 _curIndex++;
572 }
573 outStreamSpec->ReleaseStream();
574 RINOK(extractCallback->SetOperationResult(opRes));
575 }
576 return S_OK;
577 COM_TRY_END
578}
579
580class CSparseStream:
581 public IInStream,
582 public CMyUnknownImp
583{
584 UInt64 _phyPos;
585 UInt64 _virtPos;
586 bool _needStartSeek;
587
588public:
589 CHandler *Handler;
590 CMyComPtr<IUnknown> HandlerRef;
591 unsigned ItemIndex;
592 CRecordVector<UInt64> PhyOffsets;
593
594 MY_UNKNOWN_IMP2(ISequentialInStream, IInStream)
595 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
596 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
597
598 void Init()
599 {
600 _virtPos = 0;
601 _phyPos = 0;
602 _needStartSeek = true;
603 }
604};
605
606
607STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize)
608{
609 if (processedSize)
610 *processedSize = 0;
611 if (size == 0)
612 return S_OK;
613 const CItemEx &item = Handler->_items[ItemIndex];
614 if (_virtPos >= item.Size)
615 return S_OK;
616 {
617 UInt64 rem = item.Size - _virtPos;
618 if (size > rem)
619 size = (UInt32)rem;
620 }
621
622 HRESULT res = S_OK;
623
624 if (item.SparseBlocks.IsEmpty())
625 memset(data, 0, size);
626 else
627 {
628 unsigned left = 0, right = item.SparseBlocks.Size();
629 for (;;)
630 {
631 unsigned mid = (left + right) / 2;
632 if (mid == left)
633 break;
634 if (_virtPos < item.SparseBlocks[mid].Offset)
635 right = mid;
636 else
637 left = mid;
638 }
639
640 const CSparseBlock &sb = item.SparseBlocks[left];
641 UInt64 relat = _virtPos - sb.Offset;
642
643 if (_virtPos >= sb.Offset && relat < sb.Size)
644 {
645 UInt64 rem = sb.Size - relat;
646 if (size > rem)
647 size = (UInt32)rem;
648 UInt64 phyPos = PhyOffsets[left] + relat;
649 if (_needStartSeek || _phyPos != phyPos)
650 {
651 RINOK(Handler->_stream->Seek((Int64)(item.GetDataPosition() + phyPos), STREAM_SEEK_SET, NULL));
652 _needStartSeek = false;
653 _phyPos = phyPos;
654 }
655 res = Handler->_stream->Read(data, size, &size);
656 _phyPos += size;
657 }
658 else
659 {
660 UInt64 next = item.Size;
661 if (_virtPos < sb.Offset)
662 next = sb.Offset;
663 else if (left + 1 < item.SparseBlocks.Size())
664 next = item.SparseBlocks[left + 1].Offset;
665 UInt64 rem = next - _virtPos;
666 if (size > rem)
667 size = (UInt32)rem;
668 memset(data, 0, size);
669 }
670 }
671
672 _virtPos += size;
673 if (processedSize)
674 *processedSize = size;
675 return res;
676}
677
678STDMETHODIMP CSparseStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
679{
680 switch (seekOrigin)
681 {
682 case STREAM_SEEK_SET: break;
683 case STREAM_SEEK_CUR: offset += _virtPos; break;
684 case STREAM_SEEK_END: offset += Handler->_items[ItemIndex].Size; break;
685 default: return STG_E_INVALIDFUNCTION;
686 }
687 if (offset < 0)
688 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
689 _virtPos = (UInt64)offset;
690 if (newPosition)
691 *newPosition = _virtPos;
692 return S_OK;
693}
694
695STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
696{
697 COM_TRY_BEGIN
698
699 const CItemEx &item = _items[index];
700
701 if (item.IsSparse())
702 {
703 CSparseStream *streamSpec = new CSparseStream;
704 CMyComPtr<IInStream> streamTemp = streamSpec;
705 streamSpec->Init();
706 streamSpec->Handler = this;
707 streamSpec->HandlerRef = (IInArchive *)this;
708 streamSpec->ItemIndex = index;
709 streamSpec->PhyOffsets.Reserve(item.SparseBlocks.Size());
710 UInt64 offs = 0;
711 FOR_VECTOR(i, item.SparseBlocks)
712 {
713 const CSparseBlock &sb = item.SparseBlocks[i];
714 streamSpec->PhyOffsets.AddInReserved(offs);
715 offs += sb.Size;
716 }
717 *stream = streamTemp.Detach();
718 return S_OK;
719 }
720
721 if (item.IsSymLink())
722 {
723 Create_BufInStream_WithReference((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this, stream);
724 return S_OK;
725 }
726
727 return CreateLimitedInStream(_stream, item.GetDataPosition(), item.PackSize, stream);
728
729 COM_TRY_END
730}
731
732void CHandler::Init()
733{
734 _forceCodePage = false;
735 _curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP;
736 _thereIsPaxExtendedHeader = false;
737}
738
739STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
740{
741 Init();
742
743 for (UInt32 i = 0; i < numProps; i++)
744 {
745 UString name = names[i];
746 name.MakeLower_Ascii();
747 if (name.IsEmpty())
748 return E_INVALIDARG;
749
750 const PROPVARIANT &prop = values[i];
751
752 if (name[0] == L'x')
753 {
754 // some clients write 'x' property. So we support it
755 UInt32 level = 0;
756 RINOK(ParsePropToUInt32(name.Ptr(1), prop, level));
757 }
758 else if (name.IsEqualTo("cp"))
759 {
760 UInt32 cp = CP_OEMCP;
761 RINOK(ParsePropToUInt32(L"", prop, cp));
762 _forceCodePage = true;
763 _curCodePage = _specifiedCodePage = cp;
764 }
765 else if (name.IsPrefixedBy_Ascii_NoCase("mt"))
766 {
767 }
768 else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
769 {
770 }
771 else
772 return E_INVALIDARG;
773 }
774 return S_OK;
775}
776
777}}
diff --git a/CPP/7zip/Archive/Tar/TarHandler.h b/CPP/7zip/Archive/Tar/TarHandler.h
new file mode 100644
index 0000000..4834c2a
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarHandler.h
@@ -0,0 +1,81 @@
1// TarHandler.h
2
3#ifndef __TAR_HANDLER_H
4#define __TAR_HANDLER_H
5
6#include "../../../Common/MyCom.h"
7
8#include "../../../Windows/PropVariant.h"
9
10#include "../../Compress/CopyCoder.h"
11
12#include "../IArchive.h"
13
14#include "TarIn.h"
15
16namespace NArchive {
17namespace NTar {
18
19class CHandler:
20 public IInArchive,
21 public IArchiveOpenSeq,
22 public IInArchiveGetStream,
23 public ISetProperties,
24 public IOutArchive,
25 public CMyUnknownImp
26{
27public:
28 CObjectVector<CItemEx> _items;
29 CMyComPtr<IInStream> _stream;
30 CMyComPtr<ISequentialInStream> _seqStream;
31private:
32 UInt32 _curIndex;
33 bool _latestIsRead;
34 CItemEx _latestItem;
35
36 UInt64 _phySize;
37 UInt64 _headersSize;
38 bool _phySizeDefined;
39 EErrorType _error;
40 bool _warning;
41 bool _isArc;
42
43 // bool _isSparse;
44 bool _thereIsPaxExtendedHeader;
45
46 bool _forceCodePage;
47 UInt32 _specifiedCodePage;
48 UInt32 _curCodePage;
49 UInt32 _openCodePage;
50
51 CEncodingCharacts _encodingCharacts;
52
53 NCompress::CCopyCoder *copyCoderSpec;
54 CMyComPtr<ICompressCoder> copyCoder;
55
56 HRESULT ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo);
57 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
58 HRESULT SkipTo(UInt32 index);
59 void TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs = false) const;
60public:
61 MY_UNKNOWN_IMP5(
62 IInArchive,
63 IArchiveOpenSeq,
64 IInArchiveGetStream,
65 ISetProperties,
66 IOutArchive
67 )
68
69 INTERFACE_IInArchive(;)
70 INTERFACE_IOutArchive(;)
71 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
72 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
73 STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
74
75 void Init();
76 CHandler();
77};
78
79}}
80
81#endif
diff --git a/CPP/7zip/Archive/Tar/TarHandlerOut.cpp b/CPP/7zip/Archive/Tar/TarHandlerOut.cpp
new file mode 100644
index 0000000..5ddb4b2
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarHandlerOut.cpp
@@ -0,0 +1,183 @@
1// TarHandlerOut.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/ComTry.h"
6#include "../../../Common/Defs.h"
7#include "../../../Common/MyLinux.h"
8#include "../../../Common/StringConvert.h"
9#include "../../../Common/UTFConvert.h"
10
11#include "../../../Windows/PropVariant.h"
12#include "../../../Windows/TimeUtils.h"
13
14#include "TarHandler.h"
15#include "TarUpdate.h"
16
17using namespace NWindows;
18
19namespace NArchive {
20namespace NTar {
21
22STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
23{
24 *type = NFileTimeType::kUnix;
25 return S_OK;
26}
27
28HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
29 UINT codePage, unsigned utfFlags, bool convertSlash)
30{
31 NCOM::CPropVariant prop;
32 RINOK(callback->GetProperty(index, propId, &prop));
33
34 if (prop.vt == VT_BSTR)
35 {
36 UString s = prop.bstrVal;
37 if (convertSlash)
38 NItemName::ReplaceSlashes_OsToUnix(s);
39
40 if (codePage == CP_UTF8)
41 {
42 ConvertUnicodeToUTF8_Flags(s, res, utfFlags);
43 // if (!ConvertUnicodeToUTF8(s, res)) // return E_INVALIDARG;
44 }
45 else
46 UnicodeStringToMultiByte2(res, s, codePage);
47 }
48 else if (prop.vt != VT_EMPTY)
49 return E_INVALIDARG;
50
51 return S_OK;
52}
53
54
55// sort old files with original order.
56
57static int CompareUpdateItems(void *const *p1, void *const *p2, void *)
58{
59 const CUpdateItem &u1 = *(*((const CUpdateItem *const *)p1));
60 const CUpdateItem &u2 = *(*((const CUpdateItem *const *)p2));
61 if (!u1.NewProps)
62 {
63 if (u2.NewProps)
64 return -1;
65 return MyCompare(u1.IndexInArc, u2.IndexInArc);
66 }
67 if (!u2.NewProps)
68 return 1;
69 return MyCompare(u1.IndexInClient, u2.IndexInClient);
70}
71
72
73STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
74 IArchiveUpdateCallback *callback)
75{
76 COM_TRY_BEGIN
77
78 if ((_stream && (_error != k_ErrorType_OK || _warning /* || _isSparse */)) || _seqStream)
79 return E_NOTIMPL;
80 CObjectVector<CUpdateItem> updateItems;
81 const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage);
82 const unsigned utfFlags = g_Unicode_To_UTF8_Flags;
83 /*
84 // for debug only:
85 unsigned utfFlags = 0;
86 utfFlags |= UTF_FLAG__TO_UTF8__EXTRACT_BMP_ESCAPE;
87 utfFlags |= UTF_FLAG__TO_UTF8__SURROGATE_ERROR;
88 */
89
90 for (UInt32 i = 0; i < numItems; i++)
91 {
92 CUpdateItem ui;
93 Int32 newData;
94 Int32 newProps;
95 UInt32 indexInArc;
96
97 if (!callback)
98 return E_FAIL;
99
100 RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArc));
101
102 ui.NewProps = IntToBool(newProps);
103 ui.NewData = IntToBool(newData);
104 ui.IndexInArc = (int)indexInArc;
105 ui.IndexInClient = i;
106
107 if (IntToBool(newProps))
108 {
109 {
110 NCOM::CPropVariant prop;
111 RINOK(callback->GetProperty(i, kpidIsDir, &prop));
112 if (prop.vt == VT_EMPTY)
113 ui.IsDir = false;
114 else if (prop.vt != VT_BOOL)
115 return E_INVALIDARG;
116 else
117 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
118 }
119
120 {
121 NCOM::CPropVariant prop;
122 RINOK(callback->GetProperty(i, kpidPosixAttrib, &prop));
123 if (prop.vt == VT_EMPTY)
124 ui.Mode =
125 MY_LIN_S_IRWXO
126 | MY_LIN_S_IRWXG
127 | MY_LIN_S_IRWXU
128 | (ui.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG);
129 else if (prop.vt != VT_UI4)
130 return E_INVALIDARG;
131 else
132 ui.Mode = prop.ulVal;
133 // 21.07 : we clear high file type bits as GNU TAR.
134 ui.Mode &= ~(UInt32)MY_LIN_S_IFMT;
135 }
136
137 {
138 NCOM::CPropVariant prop;
139 RINOK(callback->GetProperty(i, kpidMTime, &prop));
140 if (prop.vt == VT_EMPTY)
141 ui.MTime = 0;
142 else if (prop.vt != VT_FILETIME)
143 return E_INVALIDARG;
144 else
145 ui.MTime = NTime::FileTimeToUnixTime64(prop.filetime);
146 }
147
148 RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true));
149 if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
150 ui.Name += '/';
151 RINOK(GetPropString(callback, i, kpidUser, ui.User, codePage, utfFlags, false));
152 RINOK(GetPropString(callback, i, kpidGroup, ui.Group, codePage, utfFlags, false));
153 }
154
155 if (IntToBool(newData))
156 {
157 NCOM::CPropVariant prop;
158 RINOK(callback->GetProperty(i, kpidSize, &prop));
159 if (prop.vt != VT_UI8)
160 return E_INVALIDARG;
161 ui.Size = prop.uhVal.QuadPart;
162 /*
163 // now we support GNU extension for big files
164 if (ui.Size >= ((UInt64)1 << 33))
165 return E_INVALIDARG;
166 */
167 }
168
169 updateItems.Add(ui);
170 }
171
172 if (_thereIsPaxExtendedHeader)
173 {
174 // we restore original order of files, if there is pax header block
175 updateItems.Sort(CompareUpdateItems, NULL);
176 }
177
178 return UpdateArchive(_stream, outStream, _items, updateItems, codePage, utfFlags, callback);
179
180 COM_TRY_END
181}
182
183}}
diff --git a/CPP/7zip/Archive/Tar/TarHeader.cpp b/CPP/7zip/Archive/Tar/TarHeader.cpp
new file mode 100644
index 0000000..9c16c89
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarHeader.cpp
@@ -0,0 +1,26 @@
1// Archive/TarHeader.cpp
2
3#include "StdAfx.h"
4
5#include "TarHeader.h"
6
7namespace NArchive {
8namespace NTar {
9namespace NFileHeader {
10
11 const char * const kLongLink = "././@LongLink";
12 const char * const kLongLink2 = "@LongLink";
13
14 // The magic field is filled with this if uname and gname are valid.
15 namespace NMagic
16 {
17 // const char * const kUsTar = "ustar"; // 5 chars
18 // const char * const kGNUTar = "GNUtar "; // 7 chars and a null
19 // const char * const kEmpty = "\0\0\0\0\0\0\0\0";
20 // 7-Zip used kUsTar_00 before 21.07:
21 // const char kUsTar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ;
22 // GNU TAR uses such header:
23 const char kUsTar_GNU[8] = { 'u', 's', 't', 'a', 'r', ' ', ' ', 0 } ;
24 }
25
26}}}
diff --git a/CPP/7zip/Archive/Tar/TarHeader.h b/CPP/7zip/Archive/Tar/TarHeader.h
new file mode 100644
index 0000000..b0f0ec3
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarHeader.h
@@ -0,0 +1,86 @@
1// Archive/TarHeader.h
2
3#ifndef __ARCHIVE_TAR_HEADER_H
4#define __ARCHIVE_TAR_HEADER_H
5
6#include "../../../Common/MyTypes.h"
7
8namespace NArchive {
9namespace NTar {
10
11namespace NFileHeader
12{
13 const unsigned kRecordSize = 512;
14 const unsigned kNameSize = 100;
15 const unsigned kUserNameSize = 32;
16 const unsigned kGroupNameSize = 32;
17 const unsigned kPrefixSize = 155;
18
19 const unsigned kUstarMagic_Offset = 257;
20
21 /*
22 struct CHeader
23 {
24 char Name[kNameSize];
25 char Mode[8];
26 char UID[8];
27 char GID[8];
28 char Size[12];
29 char ModificationTime[12];
30 char CheckSum[8];
31 char LinkFlag;
32 char LinkName[kNameSize];
33 char Magic[8];
34 char UserName[kUserNameSize];
35 char GroupName[kGroupNameSize];
36 char DeviceMajor[8];
37 char DeviceMinor[8];
38 char Prefix[155];
39 };
40 union CRecord
41 {
42 CHeader Header;
43 Byte Padding[kRecordSize];
44 };
45 */
46
47 namespace NLinkFlag
48 {
49 const char kOldNormal = 0; // Normal disk file, Unix compatible
50 const char kNormal = '0'; // Normal disk file
51 const char kHardLink = '1'; // Link to previously dumped file
52 const char kSymLink = '2'; // Symbolic link
53 const char kCharacter = '3'; // Character special file
54 const char kBlock = '4'; // Block special file
55 const char kDirectory = '5'; // Directory
56 const char kFIFO = '6'; // FIFO special file
57 const char kContiguous = '7'; // Contiguous file
58 const char kGnu_LongLink = 'K';
59 const char kGnu_LongName = 'L';
60 const char kSparse = 'S';
61 const char kLabel = 'V';
62 const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR.
63 data: list of files created by the --incremental (-G) option
64 Each file name is preceded by either
65 - 'Y' (file should be in this archive)
66 - 'N' (file is a directory, or is not stored in the archive.)
67 Each file name is terminated by a null + an additional null after
68 the last file name. */
69 }
70
71 extern const char * const kLongLink; // = "././@LongLink";
72 extern const char * const kLongLink2; // = "@LongLink";
73
74 namespace NMagic
75 {
76 // extern const char * const kUsTar; // = "ustar"; // 5 chars
77 // extern const char * const kGNUTar; // = "GNUtar "; // 7 chars and a null
78 // extern const char * const kEmpty; // = "\0\0\0\0\0\0\0\0"
79 // extern const char kUsTar_00[8];
80 extern const char kUsTar_GNU[8];
81 }
82}
83
84}}
85
86#endif
diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp
new file mode 100644
index 0000000..58399d0
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarIn.cpp
@@ -0,0 +1,517 @@
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
15namespace NArchive {
16namespace NTar {
17
18static 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
29static 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
44static 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/*
56static 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
65static 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
73static void ReadString(const char *s, unsigned size, AString &result)
74{
75 result.SetFrom_CalcLen(s, size);
76}
77
78static bool ParseInt64(const char *p, Int64 &val)
79{
80 UInt32 h = GetBe32(p);
81 val = (Int64)GetBe64(p + 4);
82 if (h == (UInt32)1 << 31)
83 return ((val >> 63) & 1) == 0;
84 if (h == (UInt32)(Int32)-1)
85 return ((val >> 63) & 1) != 0;
86 UInt64 uv;
87 bool res = OctalToNumber(p, 12, uv);
88 val = (Int64)uv;
89 return res;
90}
91
92static bool ParseInt64_MTime(const char *p, Int64 &val)
93{
94 // rare case tar : ZEROs in Docker-Windows TARs
95 // rare case tar : spaces
96 if (GetUi32(p) != 0)
97 for (unsigned i = 0; i < 12; i++)
98 if (p[i] != ' ')
99 return ParseInt64(p, val);
100 val = 0;
101 return true;
102}
103
104static bool ParseSize(const char *p, UInt64 &val)
105{
106 if (GetBe32(p) == (UInt32)1 << 31)
107 {
108 // GNU extension
109 val = GetBe64(p + 4);
110 return ((val >> 63) & 1) == 0;
111 }
112 return OctalToNumber(p, 12, val,
113 true // 20.03: allow empty size for 'V' Label entry
114 );
115}
116
117#define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
118
119API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
120{
121 if (size < NFileHeader::kRecordSize)
122 return k_IsArc_Res_NEED_MORE;
123
124 const char *p = (const char *)p2;
125 p += NFileHeader::kNameSize;
126
127 UInt32 mode;
128 // we allow empty Mode value for LongName prefix items
129 CHECK(OctalToNumber32(p, 8, mode, true)); p += 8;
130
131 // if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0;
132 p += 8;
133 // if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0;
134 p += 8;
135
136 UInt64 packSize;
137 Int64 time;
138 UInt32 checkSum;
139 CHECK(ParseSize(p, packSize)); p += 12;
140 CHECK(ParseInt64_MTime(p, time)); p += 12;
141 CHECK(OctalToNumber32(p, 8, checkSum));
142 return k_IsArc_Res_YES;
143}
144
145static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
146{
147 char buf[NFileHeader::kRecordSize];
148 char *p = buf;
149
150 error = k_ErrorType_OK;
151 filled = false;
152
153 bool thereAreEmptyRecords = false;
154 for (;;)
155 {
156 size_t processedSize = NFileHeader::kRecordSize;
157 RINOK(ReadStream(stream, buf, &processedSize));
158 if (processedSize == 0)
159 {
160 if (!thereAreEmptyRecords)
161 error = k_ErrorType_UnexpectedEnd; // "There are no trailing zero-filled records";
162 return S_OK;
163 }
164 if (processedSize != NFileHeader::kRecordSize)
165 {
166 if (!thereAreEmptyRecords)
167 error = k_ErrorType_UnexpectedEnd; // error = "There is no correct record at the end of archive";
168 else
169 {
170 /*
171 if (IsEmptyData(buf, processedSize))
172 error = k_ErrorType_UnexpectedEnd;
173 else
174 {
175 // extraReadSize = processedSize;
176 // error = k_ErrorType_Corrupted; // some data after the end tail zeros
177 }
178 */
179 }
180
181 return S_OK;
182 }
183 if (!IsRecordLast(buf))
184 break;
185 item.HeaderSize += NFileHeader::kRecordSize;
186 thereAreEmptyRecords = true;
187 }
188 if (thereAreEmptyRecords)
189 {
190 // error = "There are data after end of archive";
191 return S_OK;
192 }
193
194 error = k_ErrorType_Corrupted;
195 ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize;
196 item.NameCouldBeReduced =
197 (item.Name.Len() == NFileHeader::kNameSize ||
198 item.Name.Len() == NFileHeader::kNameSize - 1);
199
200 // we allow empty Mode value for LongName prefix items
201 RIF(OctalToNumber32(p, 8, item.Mode, true)); p += 8;
202
203 if (!OctalToNumber32(p, 8, item.UID)) { item.UID = 0; } p += 8;
204 if (!OctalToNumber32(p, 8, item.GID)) { item.GID = 0; } p += 8;
205
206 RIF(ParseSize(p, item.PackSize));
207 item.Size = item.PackSize;
208 p += 12;
209 RIF(ParseInt64_MTime(p, item.MTime)); p += 12;
210
211 UInt32 checkSum;
212 RIF(OctalToNumber32(p, 8, checkSum));
213 memset(p, ' ', 8); p += 8;
214
215 item.LinkFlag = *p++;
216
217 ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
218 item.LinkNameCouldBeReduced =
219 (item.LinkName.Len() == NFileHeader::kNameSize ||
220 item.LinkName.Len() == NFileHeader::kNameSize - 1);
221
222 memcpy(item.Magic, p, 8); p += 8;
223
224 ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
225 ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
226
227 item.DeviceMajorDefined = (p[0] != 0); if (item.DeviceMajorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMajor)); } p += 8;
228 item.DeviceMinorDefined = (p[0] != 0); if (item.DeviceMinorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMinor)); } p += 8;
229
230 if (p[0] != 0)
231 {
232 AString prefix;
233 ReadString(p, NFileHeader::kPrefixSize, prefix);
234 if (!prefix.IsEmpty()
235 && item.IsUstarMagic()
236 && (item.LinkFlag != 'L' /* || prefix != "00000000000" */ ))
237 item.Name = prefix + '/' + item.Name;
238 }
239
240 p += NFileHeader::kPrefixSize;
241
242 if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
243 {
244 item.PackSize = 0;
245 item.Size = 0;
246 }
247
248 if (item.LinkFlag == NFileHeader::NLinkFlag::kDirectory)
249 {
250 // GNU tar ignores Size field, if LinkFlag is kDirectory
251 // 21.02 : we set PackSize = 0 to be more compatible with GNU tar
252 item.PackSize = 0;
253 // item.Size = 0;
254 }
255
256 /*
257 TAR standard requires sum of unsigned byte values.
258 But some TAR programs use sum of signed byte values.
259 So we check both values.
260 */
261 UInt32 checkSumReal = 0;
262 Int32 checkSumReal_Signed = 0;
263 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
264 {
265 char c = buf[i];
266 checkSumReal_Signed += (signed char)c;
267 checkSumReal += (Byte)buf[i];
268 }
269
270 if (checkSumReal != checkSum)
271 {
272 if ((UInt32)checkSumReal_Signed != checkSum)
273 return S_OK;
274 }
275
276 item.HeaderSize += NFileHeader::kRecordSize;
277
278 if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse)
279 {
280 Byte isExtended = (Byte)buf[482];
281 if (isExtended != 0 && isExtended != 1)
282 return S_OK;
283 RIF(ParseSize(buf + 483, item.Size));
284 UInt64 min = 0;
285 for (unsigned i = 0; i < 4; i++)
286 {
287 p = buf + 386 + 24 * i;
288 if (GetBe32(p) == 0)
289 {
290 if (isExtended != 0)
291 return S_OK;
292 break;
293 }
294 CSparseBlock sb;
295 RIF(ParseSize(p, sb.Offset));
296 RIF(ParseSize(p + 12, sb.Size));
297 item.SparseBlocks.Add(sb);
298 if (sb.Offset < min || sb.Offset > item.Size)
299 return S_OK;
300 if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
301 return S_OK;
302 min = sb.Offset + sb.Size;
303 if (min < sb.Offset)
304 return S_OK;
305 }
306 if (min > item.Size)
307 return S_OK;
308
309 while (isExtended != 0)
310 {
311 size_t processedSize = NFileHeader::kRecordSize;
312 RINOK(ReadStream(stream, buf, &processedSize));
313 if (processedSize != NFileHeader::kRecordSize)
314 {
315 error = k_ErrorType_UnexpectedEnd;
316 return S_OK;
317 }
318
319 item.HeaderSize += NFileHeader::kRecordSize;
320 isExtended = (Byte)buf[21 * 24];
321 if (isExtended != 0 && isExtended != 1)
322 return S_OK;
323 for (unsigned i = 0; i < 21; i++)
324 {
325 p = buf + 24 * i;
326 if (GetBe32(p) == 0)
327 {
328 if (isExtended != 0)
329 return S_OK;
330 break;
331 }
332 CSparseBlock sb;
333 RIF(ParseSize(p, sb.Offset));
334 RIF(ParseSize(p + 12, sb.Size));
335 item.SparseBlocks.Add(sb);
336 if (sb.Offset < min || sb.Offset > item.Size)
337 return S_OK;
338 if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0)
339 return S_OK;
340 min = sb.Offset + sb.Size;
341 if (min < sb.Offset)
342 return S_OK;
343 }
344 }
345 if (min > item.Size)
346 return S_OK;
347 }
348
349 filled = true;
350 error = k_ErrorType_OK;
351 return S_OK;
352}
353
354
355static HRESULT ReadDataToString(ISequentialInStream *stream, CItemEx &item, AString &s, EErrorType &error)
356{
357 const unsigned packSize = (unsigned)item.GetPackSizeAligned();
358 size_t processedSize = packSize;
359 HRESULT res = ReadStream(stream, s.GetBuf(packSize), &processedSize);
360 item.HeaderSize += (unsigned)processedSize;
361 s.ReleaseBuf_CalcLen((unsigned)item.PackSize);
362 RINOK(res);
363 if (processedSize != packSize)
364 error = k_ErrorType_UnexpectedEnd;
365 return S_OK;
366}
367
368static bool ParsePaxLongName(const AString &src, AString &dest)
369{
370 dest.Empty();
371 for (unsigned pos = 0;;)
372 {
373 if (pos >= src.Len())
374 return false;
375 const char *start = src.Ptr(pos);
376 const char *end;
377 const UInt32 lineLen = ConvertStringToUInt32(start, &end);
378 if (end == start)
379 return false;
380 if (*end != ' ')
381 return false;
382 if (lineLen > src.Len() - pos)
383 return false;
384 unsigned offset = (unsigned)(end - start) + 1;
385 if (lineLen < offset)
386 return false;
387 if (IsString1PrefixedByString2(src.Ptr(pos + offset), "path="))
388 {
389 offset += 5; // "path="
390 dest = src.Mid(pos + offset, lineLen - offset);
391 if (dest.IsEmpty())
392 return false;
393 if (dest.Back() != '\n')
394 return false;
395 dest.DeleteBack();
396 return true;
397 }
398 pos += lineLen;
399 }
400}
401
402HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error)
403{
404 item.HeaderSize = 0;
405
406 bool flagL = false;
407 bool flagK = false;
408 AString nameL;
409 AString nameK;
410 AString pax;
411
412 for (;;)
413 {
414 RINOK(GetNextItemReal(stream, filled, item, error));
415 if (!filled)
416 {
417 if (error == k_ErrorType_OK && (flagL || flagK))
418 error = k_ErrorType_Corrupted;
419 return S_OK;
420 }
421
422 if (error != k_ErrorType_OK)
423 return S_OK;
424
425 if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName || // file contains a long name
426 item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongLink) // file contains a long linkname
427 {
428 AString *name;
429 if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName)
430 { if (flagL) return S_OK; flagL = true; name = &nameL; }
431 else
432 { if (flagK) return S_OK; flagK = true; name = &nameK; }
433
434 if (item.Name != NFileHeader::kLongLink &&
435 item.Name != NFileHeader::kLongLink2)
436 return S_OK;
437 if (item.PackSize > (1 << 14))
438 return S_OK;
439
440 RINOK(ReadDataToString(stream, item, *name, error));
441 if (error != k_ErrorType_OK)
442 return S_OK;
443
444 continue;
445 }
446
447 switch (item.LinkFlag)
448 {
449 case 'g':
450 case 'x':
451 case 'X':
452 {
453 const char *s = item.Name.Ptr();
454 if (IsString1PrefixedByString2(s, "./"))
455 s += 2;
456 if (IsString1PrefixedByString2(s, "./"))
457 s += 2;
458 if ( IsString1PrefixedByString2(s, "PaxHeader/")
459 || IsString1PrefixedByString2(s, "PaxHeaders.X/")
460 || IsString1PrefixedByString2(s, "PaxHeaders.4467/")
461 || StringsAreEqual_Ascii(s, "@PaxHeader")
462 )
463 {
464 RINOK(ReadDataToString(stream, item, pax, error));
465 if (error != k_ErrorType_OK)
466 return S_OK;
467 continue;
468 }
469 break;
470 }
471 case NFileHeader::NLinkFlag::kDumpDir:
472 {
473 break;
474 // GNU Extensions to the Archive Format
475 }
476 case NFileHeader::NLinkFlag::kSparse:
477 {
478 break;
479 // GNU Extensions to the Archive Format
480 }
481 default:
482 if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
483 return S_OK;
484 }
485
486 if (flagL)
487 {
488 item.Name = nameL;
489 item.NameCouldBeReduced = false;
490 }
491
492 if (flagK)
493 {
494 item.LinkName = nameK;
495 item.LinkNameCouldBeReduced = false;
496 }
497
498 error = k_ErrorType_OK;
499
500 if (!pax.IsEmpty())
501 {
502 AString name;
503 if (ParsePaxLongName(pax, name))
504 item.Name = name;
505 else
506 {
507 // no "path" property is allowed in pax4467
508 // error = k_ErrorType_Warning;
509 }
510 pax.Empty();
511 }
512
513 return S_OK;
514 }
515}
516
517}}
diff --git a/CPP/7zip/Archive/Tar/TarIn.h b/CPP/7zip/Archive/Tar/TarIn.h
new file mode 100644
index 0000000..1c508bc
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarIn.h
@@ -0,0 +1,27 @@
1// TarIn.h
2
3#ifndef __ARCHIVE_TAR_IN_H
4#define __ARCHIVE_TAR_IN_H
5
6#include "../../IStream.h"
7
8#include "TarItem.h"
9
10namespace NArchive {
11namespace NTar {
12
13enum EErrorType
14{
15 k_ErrorType_OK,
16 k_ErrorType_Corrupted,
17 k_ErrorType_UnexpectedEnd,
18 k_ErrorType_Warning
19};
20
21HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, EErrorType &error);
22
23API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size);
24
25}}
26
27#endif
diff --git a/CPP/7zip/Archive/Tar/TarItem.h b/CPP/7zip/Archive/Tar/TarItem.h
new file mode 100644
index 0000000..f947786
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarItem.h
@@ -0,0 +1,182 @@
1// TarItem.h
2
3#ifndef __ARCHIVE_TAR_ITEM_H
4#define __ARCHIVE_TAR_ITEM_H
5
6#include "../../../Common/MyLinux.h"
7#include "../../../Common/UTFConvert.h"
8
9#include "../Common/ItemNameUtils.h"
10
11#include "TarHeader.h"
12
13namespace NArchive {
14namespace NTar {
15
16struct CSparseBlock
17{
18 UInt64 Offset;
19 UInt64 Size;
20};
21
22struct CItem
23{
24 AString Name;
25 UInt64 PackSize;
26 UInt64 Size;
27 Int64 MTime;
28
29 UInt32 Mode;
30 UInt32 UID;
31 UInt32 GID;
32 UInt32 DeviceMajor;
33 UInt32 DeviceMinor;
34
35 AString LinkName;
36 AString User;
37 AString Group;
38
39 char Magic[8];
40 char LinkFlag;
41 bool DeviceMajorDefined;
42 bool DeviceMinorDefined;
43
44 CRecordVector<CSparseBlock> SparseBlocks;
45
46 void SetDefaultWriteFields()
47 {
48 DeviceMajorDefined = false;
49 DeviceMinorDefined = false;
50 UID = 0;
51 GID = 0;
52 memcpy(Magic, NFileHeader::NMagic::kUsTar_GNU, 8);
53 }
54
55 bool IsSymLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymLink && (Size == 0); }
56 bool IsHardLink() const { return LinkFlag == NFileHeader::NLinkFlag::kHardLink; }
57 bool IsSparse() const { return LinkFlag == NFileHeader::NLinkFlag::kSparse; }
58 UInt64 GetUnpackSize() const { return IsSymLink() ? LinkName.Len() : Size; }
59 bool IsPaxExtendedHeader() const
60 {
61 switch (LinkFlag)
62 {
63 case 'g':
64 case 'x':
65 case 'X': // Check it
66 return true;
67 }
68 return false;
69 }
70
71 UInt32 Get_Combined_Mode() const
72 {
73 return (Mode & ~(UInt32)MY_LIN_S_IFMT) | Get_FileTypeMode_from_LinkFlag();
74 }
75
76 UInt32 Get_FileTypeMode_from_LinkFlag() const
77 {
78 switch (LinkFlag)
79 {
80 /*
81 case NFileHeader::NLinkFlag::kDirectory:
82 case NFileHeader::NLinkFlag::kDumpDir:
83 return MY_LIN_S_IFDIR;
84 */
85 case NFileHeader::NLinkFlag::kSymLink: return MY_LIN_S_IFLNK;
86 case NFileHeader::NLinkFlag::kBlock: return MY_LIN_S_IFBLK;
87 case NFileHeader::NLinkFlag::kCharacter: return MY_LIN_S_IFCHR;
88 case NFileHeader::NLinkFlag::kFIFO: return MY_LIN_S_IFIFO;
89 // case return MY_LIN_S_IFSOCK;
90 }
91
92 if (IsDir())
93 return MY_LIN_S_IFDIR;
94 return MY_LIN_S_IFREG;
95 }
96
97 bool IsDir() const
98 {
99 switch (LinkFlag)
100 {
101 case NFileHeader::NLinkFlag::kDirectory:
102 case NFileHeader::NLinkFlag::kDumpDir:
103 return true;
104 case NFileHeader::NLinkFlag::kOldNormal:
105 case NFileHeader::NLinkFlag::kNormal:
106 case NFileHeader::NLinkFlag::kSymLink:
107 return NItemName::HasTailSlash(Name, CP_OEMCP);
108 }
109 return false;
110 }
111
112 bool IsUstarMagic() const
113 {
114 for (int i = 0; i < 5; i++)
115 if (Magic[i] != NFileHeader::NMagic::kUsTar_GNU[i])
116 return false;
117 return true;
118 }
119
120 UInt64 GetPackSizeAligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); }
121
122 bool IsThereWarning() const
123 {
124 // that Header Warning is possible if (Size != 0) for dir item
125 return (PackSize < Size) && (LinkFlag == NFileHeader::NLinkFlag::kDirectory);
126 }
127};
128
129
130
131struct CEncodingCharacts
132{
133 bool IsAscii;
134 // bool Oem_Checked;
135 // bool Oem_Ok;
136 // bool Utf_Checked;
137 CUtf8Check UtfCheck;
138
139 void Clear()
140 {
141 IsAscii = true;
142 // Oem_Checked = false;
143 // Oem_Ok = false;
144 // Utf_Checked = false;
145 UtfCheck.Clear();
146 }
147
148 void Update(const CEncodingCharacts &ec)
149 {
150 if (!ec.IsAscii)
151 IsAscii = false;
152
153 // if (ec.Utf_Checked)
154 {
155 UtfCheck.Update(ec.UtfCheck);
156 // Utf_Checked = true;
157 }
158 }
159
160 CEncodingCharacts() { Clear(); }
161 void Check(const AString &s);
162 AString GetCharactsString() const;
163};
164
165
166
167struct CItemEx: public CItem
168{
169 UInt64 HeaderPos;
170 unsigned HeaderSize;
171 bool NameCouldBeReduced;
172 bool LinkNameCouldBeReduced;
173
174 CEncodingCharacts EncodingCharacts;
175
176 UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
177 UInt64 GetFullSize() const { return HeaderSize + PackSize; }
178};
179
180}}
181
182#endif
diff --git a/CPP/7zip/Archive/Tar/TarOut.cpp b/CPP/7zip/Archive/Tar/TarOut.cpp
new file mode 100644
index 0000000..271b854
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarOut.cpp
@@ -0,0 +1,279 @@
1// TarOut.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/StreamUtils.h"
6
7#include "TarOut.h"
8
9namespace NArchive {
10namespace NTar {
11
12HRESULT COutArchive::WriteBytes(const void *data, unsigned size)
13{
14 Pos += size;
15 return WriteStream(m_Stream, data, size);
16}
17
18static bool WriteOctal_8(char *s, UInt32 val)
19{
20 const unsigned kNumDigits = 8 - 1;
21 if (val >= ((UInt32)1 << (kNumDigits * 3)))
22 return false;
23 for (unsigned i = 0; i < kNumDigits; i++)
24 {
25 s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
26 val >>= 3;
27 }
28 return true;
29}
30
31static void WriteBin_64bit(char *s, UInt64 val)
32{
33 for (unsigned i = 0; i < 8; i++, val <<= 8)
34 s[i] = (char)(val >> 56);
35}
36
37static void WriteOctal_12(char *s, UInt64 val)
38{
39 const unsigned kNumDigits = 12 - 1;
40 if (val >= ((UInt64)1 << (kNumDigits * 3)))
41 {
42 // GNU extension;
43 s[0] = (char)(Byte)0x80;
44 s[1] = s[2] = s[3] = 0;
45 WriteBin_64bit(s + 4, val);
46 return;
47 }
48 for (unsigned i = 0; i < kNumDigits; i++)
49 {
50 s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
51 val >>= 3;
52 }
53}
54
55static void WriteOctal_12_Signed(char *s, Int64 val)
56{
57 if (val >= 0)
58 {
59 WriteOctal_12(s, (UInt64)val);
60 return;
61 }
62 s[0] = s[1] = s[2] = s[3] = (char)(Byte)0xFF;
63 WriteBin_64bit(s + 4, val);
64}
65
66static void CopyString(char *dest, const AString &src, unsigned maxSize)
67{
68 unsigned len = src.Len();
69 if (len == 0)
70 return;
71 // 21.07: we don't require additional 0 character at the end
72 if (len > maxSize)
73 {
74 len = maxSize;
75 // return false;
76 }
77 memcpy(dest, src.Ptr(), len);
78 // return true;
79}
80
81#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; }
82
83#define COPY_STRING_CHECK(dest, src, size) \
84 CopyString(dest, src, size); dest += (size);
85
86#define WRITE_OCTAL_8_CHECK(dest, src) \
87 RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src));
88
89
90HRESULT COutArchive::WriteHeaderReal(const CItem &item)
91{
92 char record[NFileHeader::kRecordSize];
93 memset(record, 0, NFileHeader::kRecordSize);
94 char *cur = record;
95
96 COPY_STRING_CHECK (cur, item.Name, NFileHeader::kNameSize);
97
98 WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8;
99 WRITE_OCTAL_8_CHECK (cur, item.UID); cur += 8;
100 WRITE_OCTAL_8_CHECK (cur, item.GID); cur += 8;
101
102 WriteOctal_12(cur, item.PackSize); cur += 12;
103 WriteOctal_12_Signed(cur, item.MTime); cur += 12;
104
105 memset(cur, ' ', 8); // checksum field
106 cur += 8;
107
108 *cur++ = item.LinkFlag;
109
110 COPY_STRING_CHECK (cur, item.LinkName, NFileHeader::kNameSize);
111
112 memcpy(cur, item.Magic, 8);
113 cur += 8;
114
115 COPY_STRING_CHECK (cur, item.User, NFileHeader::kUserNameSize);
116 COPY_STRING_CHECK (cur, item.Group, NFileHeader::kGroupNameSize);
117
118 if (item.DeviceMajorDefined)
119 WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor);
120 cur += 8;
121 if (item.DeviceMinorDefined)
122 WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor);
123 cur += 8;
124
125 if (item.IsSparse())
126 {
127 record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0);
128 WriteOctal_12(record + 483, item.Size);
129 for (unsigned i = 0; i < item.SparseBlocks.Size() && i < 4; i++)
130 {
131 const CSparseBlock &sb = item.SparseBlocks[i];
132 char *p = record + 386 + 24 * i;
133 WriteOctal_12(p, sb.Offset);
134 WriteOctal_12(p + 12, sb.Size);
135 }
136 }
137
138 {
139 UInt32 checkSum = 0;
140 {
141 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
142 checkSum += (Byte)record[i];
143 }
144 /* we use GNU TAR scheme:
145 checksum field is formatted differently from the
146 other fields: it has [6] digits, a null, then a space. */
147 // WRITE_OCTAL_8_CHECK(record + 148, checkSum);
148 const unsigned kNumDigits = 6;
149 for (unsigned i = 0; i < kNumDigits; i++)
150 {
151 record[148 + kNumDigits - 1 - i] = (char)('0' + (checkSum & 7));
152 checkSum >>= 3;
153 }
154 record[148 + 6] = 0;
155 }
156
157 RINOK(WriteBytes(record, NFileHeader::kRecordSize));
158
159 if (item.IsSparse())
160 {
161 for (unsigned i = 4; i < item.SparseBlocks.Size();)
162 {
163 memset(record, 0, NFileHeader::kRecordSize);
164 for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++)
165 {
166 const CSparseBlock &sb = item.SparseBlocks[i];
167 char *p = record + 24 * t;
168 WriteOctal_12(p, sb.Offset);
169 WriteOctal_12(p + 12, sb.Size);
170 }
171 record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0);
172 RINOK(WriteBytes(record, NFileHeader::kRecordSize));
173 }
174 }
175
176 return S_OK;
177}
178
179
180/* OLD_GNU_TAR: writes short name with zero at the end
181 NEW_GNU_TAR: writes short name without zero at the end */
182
183static const unsigned kNameSize_Max =
184 NFileHeader::kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
185 // NFileHeader::kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
186
187#define DOES_NAME_FIT_IN_FIELD(name) ((name).Len() <= kNameSize_Max)
188
189HRESULT COutArchive::WriteHeader(const CItem &item)
190{
191 if (DOES_NAME_FIT_IN_FIELD(item.Name) &&
192 DOES_NAME_FIT_IN_FIELD(item.LinkName))
193 return WriteHeaderReal(item);
194
195 // here we can get all fields from main (item) or create new empty item
196 /*
197 CItem mi;
198 mi.SetDefaultWriteFields();
199 */
200
201 CItem mi = item;
202 mi.LinkName.Empty();
203 // SparseBlocks will be ignored by IsSparse()
204 // mi.SparseBlocks.Clear();
205
206 mi.Name = NFileHeader::kLongLink;
207 // 21.07 : we set Mode and MTime props as in GNU TAR:
208 mi.Mode = 0644; // octal
209 mi.MTime = 0;
210
211 for (int i = 0; i < 2; i++)
212 {
213 const AString *name;
214 // We suppose that GNU TAR also writes item for long link before item for LongName?
215 if (i == 0)
216 {
217 mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongLink;
218 name = &item.LinkName;
219 }
220 else
221 {
222 mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongName;
223 name = &item.Name;
224 }
225 if (DOES_NAME_FIT_IN_FIELD(*name))
226 continue;
227 // GNU TAR writes null character after NAME to file. We do same here:
228 const unsigned nameStreamSize = name->Len() + 1;
229 mi.PackSize = nameStreamSize;
230 RINOK(WriteHeaderReal(mi));
231 RINOK(WriteBytes(name->Ptr(), nameStreamSize));
232 RINOK(FillDataResidual(nameStreamSize));
233 }
234
235 // 21.07: WriteHeaderReal() writes short part of (Name) and (LinkName).
236 return WriteHeaderReal(item);
237 /*
238 mi = item;
239 if (!DOES_NAME_FIT_IN_FIELD(mi.Name))
240 mi.Name.SetFrom(item.Name, kNameSize_Max);
241 if (!DOES_NAME_FIT_IN_FIELD(mi.LinkName))
242 mi.LinkName.SetFrom(item.LinkName, kNameSize_Max);
243 return WriteHeaderReal(mi);
244 */
245}
246
247HRESULT COutArchive::FillDataResidual(UInt64 dataSize)
248{
249 unsigned lastRecordSize = ((unsigned)dataSize & (NFileHeader::kRecordSize - 1));
250 if (lastRecordSize == 0)
251 return S_OK;
252 unsigned rem = NFileHeader::kRecordSize - lastRecordSize;
253 Byte buf[NFileHeader::kRecordSize];
254 memset(buf, 0, rem);
255 return WriteBytes(buf, rem);
256}
257
258HRESULT COutArchive::WriteFinishHeader()
259{
260 Byte record[NFileHeader::kRecordSize];
261 memset(record, 0, NFileHeader::kRecordSize);
262
263 const unsigned kNumFinishRecords = 2;
264
265 /* GNU TAR by default uses --blocking-factor=20 (512 * 20 = 10 KiB)
266 we also can use cluster alignment:
267 const unsigned numBlocks = (unsigned)(Pos / NFileHeader::kRecordSize) + kNumFinishRecords;
268 const unsigned kNumClusterBlocks = (1 << 3); // 8 blocks = 4 KiB
269 const unsigned numFinishRecords = kNumFinishRecords + ((kNumClusterBlocks - numBlocks) & (kNumClusterBlocks - 1));
270 */
271
272 for (unsigned i = 0; i < kNumFinishRecords; i++)
273 {
274 RINOK(WriteBytes(record, NFileHeader::kRecordSize));
275 }
276 return S_OK;
277}
278
279}}
diff --git a/CPP/7zip/Archive/Tar/TarOut.h b/CPP/7zip/Archive/Tar/TarOut.h
new file mode 100644
index 0000000..ee9b965
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarOut.h
@@ -0,0 +1,36 @@
1// Archive/TarOut.h
2
3#ifndef __ARCHIVE_TAR_OUT_H
4#define __ARCHIVE_TAR_OUT_H
5
6#include "../../../Common/MyCom.h"
7
8#include "../../IStream.h"
9
10#include "TarItem.h"
11
12namespace NArchive {
13namespace NTar {
14
15class COutArchive
16{
17 CMyComPtr<ISequentialOutStream> m_Stream;
18
19 HRESULT WriteBytes(const void *data, unsigned size);
20 HRESULT WriteHeaderReal(const CItem &item);
21public:
22 UInt64 Pos;
23
24 void Create(ISequentialOutStream *outStream)
25 {
26 m_Stream = outStream;
27 }
28
29 HRESULT WriteHeader(const CItem &item);
30 HRESULT FillDataResidual(UInt64 dataSize);
31 HRESULT WriteFinishHeader();
32};
33
34}}
35
36#endif
diff --git a/CPP/7zip/Archive/Tar/TarRegister.cpp b/CPP/7zip/Archive/Tar/TarRegister.cpp
new file mode 100644
index 0000000..5014f04
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarRegister.cpp
@@ -0,0 +1,23 @@
1// TarRegister.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/RegisterArc.h"
6
7#include "TarHandler.h"
8
9namespace NArchive {
10namespace NTar {
11
12static const Byte k_Signature[] = { 'u', 's', 't', 'a', 'r' };
13
14REGISTER_ARC_IO(
15 "tar", "tar ova", 0, 0xEE,
16 k_Signature,
17 NFileHeader::kUstarMagic_Offset,
18 NArcInfoFlags::kStartOpen |
19 NArcInfoFlags::kSymLinks |
20 NArcInfoFlags::kHardLinks,
21 IsArc_Tar)
22
23}}
diff --git a/CPP/7zip/Archive/Tar/TarUpdate.cpp b/CPP/7zip/Archive/Tar/TarUpdate.cpp
new file mode 100644
index 0000000..295e16b
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarUpdate.cpp
@@ -0,0 +1,259 @@
1// TarUpdate.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Windows/TimeUtils.h"
6
7#include "../../Common/LimitedStreams.h"
8#include "../../Common/ProgressUtils.h"
9
10#include "../../Compress/CopyCoder.h"
11
12#include "TarOut.h"
13#include "TarUpdate.h"
14
15namespace NArchive {
16namespace NTar {
17
18HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
19 const CObjectVector<NArchive::NTar::CItemEx> &inputItems,
20 const CObjectVector<CUpdateItem> &updateItems,
21 UINT codePage, unsigned utfFlags,
22 IArchiveUpdateCallback *updateCallback)
23{
24 COutArchive outArchive;
25 outArchive.Create(outStream);
26 outArchive.Pos = 0;
27
28 CMyComPtr<IOutStream> outSeekStream;
29 outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
30
31 CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
32 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
33
34 UInt64 complexity = 0;
35
36 unsigned i;
37 for (i = 0; i < updateItems.Size(); i++)
38 {
39 const CUpdateItem &ui = updateItems[i];
40 if (ui.NewData)
41 complexity += ui.Size;
42 else
43 complexity += inputItems[(unsigned)ui.IndexInArc].GetFullSize();
44 }
45
46 RINOK(updateCallback->SetTotal(complexity));
47
48 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
49 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
50
51 CLocalProgress *lps = new CLocalProgress;
52 CMyComPtr<ICompressProgressInfo> progress = lps;
53 lps->Init(updateCallback, true);
54
55 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
56 CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
57 streamSpec->SetStream(inStream);
58
59 complexity = 0;
60
61 for (i = 0; i < updateItems.Size(); i++)
62 {
63 lps->InSize = lps->OutSize = complexity;
64 RINOK(lps->SetCur());
65
66 const CUpdateItem &ui = updateItems[i];
67 CItem item;
68
69 if (ui.NewProps)
70 {
71 item.SetDefaultWriteFields();
72 item.Mode = ui.Mode;
73 item.Name = ui.Name;
74 item.User = ui.User;
75 item.Group = ui.Group;
76
77 if (ui.IsDir)
78 {
79 item.LinkFlag = NFileHeader::NLinkFlag::kDirectory;
80 item.PackSize = 0;
81 }
82 else
83 {
84 item.LinkFlag = NFileHeader::NLinkFlag::kNormal;
85 item.PackSize = ui.Size;
86 }
87
88 item.MTime = ui.MTime;
89 }
90 else
91 item = inputItems[(unsigned)ui.IndexInArc];
92
93 AString symLink;
94 if (ui.NewData || ui.NewProps)
95 {
96 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink, codePage, utfFlags, true));
97 if (!symLink.IsEmpty())
98 {
99 item.LinkFlag = NFileHeader::NLinkFlag::kSymLink;
100 item.LinkName = symLink;
101 }
102 }
103
104 if (ui.NewData)
105 {
106 item.SparseBlocks.Clear();
107 item.PackSize = ui.Size;
108 item.Size = ui.Size;
109 if (ui.Size == (UInt64)(Int64)-1)
110 return E_INVALIDARG;
111
112 CMyComPtr<ISequentialInStream> fileInStream;
113
114 bool needWrite = true;
115
116 if (!symLink.IsEmpty())
117 {
118 item.PackSize = 0;
119 item.Size = 0;
120 }
121 else
122 {
123 HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
124
125 if (res == S_FALSE)
126 needWrite = false;
127 else
128 {
129 RINOK(res);
130
131 if (fileInStream)
132 {
133 CMyComPtr<IStreamGetProps> getProps;
134 fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
135 if (getProps)
136 {
137 FILETIME mTime;
138 UInt64 size2;
139 if (getProps->GetProps(&size2, NULL, NULL, &mTime, NULL) == S_OK)
140 {
141 item.PackSize = size2;
142 item.Size = size2;
143 item.MTime = NWindows::NTime::FileTimeToUnixTime64(mTime);;
144 }
145 }
146 }
147 else
148 {
149 item.PackSize = 0;
150 item.Size = 0;
151 }
152
153 {
154 AString hardLink;
155 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink, codePage, utfFlags, true));
156 if (!hardLink.IsEmpty())
157 {
158 item.LinkFlag = NFileHeader::NLinkFlag::kHardLink;
159 item.LinkName = hardLink;
160 item.PackSize = 0;
161 item.Size = 0;
162 fileInStream.Release();
163 }
164 }
165 }
166 }
167
168 if (needWrite)
169 {
170 UInt64 fileHeaderStartPos = outArchive.Pos;
171 RINOK(outArchive.WriteHeader(item));
172 if (fileInStream)
173 {
174 RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
175 outArchive.Pos += copyCoderSpec->TotalSize;
176 if (copyCoderSpec->TotalSize != item.PackSize)
177 {
178 if (!outSeekStream)
179 return E_FAIL;
180 UInt64 backOffset = outArchive.Pos - fileHeaderStartPos;
181 RINOK(outSeekStream->Seek(-(Int64)backOffset, STREAM_SEEK_CUR, NULL));
182 outArchive.Pos = fileHeaderStartPos;
183 item.PackSize = copyCoderSpec->TotalSize;
184 RINOK(outArchive.WriteHeader(item));
185 RINOK(outSeekStream->Seek((Int64)item.PackSize, STREAM_SEEK_CUR, NULL));
186 outArchive.Pos += item.PackSize;
187 }
188 RINOK(outArchive.FillDataResidual(item.PackSize));
189 }
190 }
191
192 complexity += item.PackSize;
193 RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
194 }
195 else
196 {
197 const CItemEx &existItem = inputItems[(unsigned)ui.IndexInArc];
198 UInt64 size;
199
200 if (ui.NewProps)
201 {
202 // memcpy(item.Magic, NFileHeader::NMagic::kEmpty, 8);
203
204 if (!symLink.IsEmpty())
205 {
206 item.PackSize = 0;
207 item.Size = 0;
208 }
209 else
210 {
211 if (ui.IsDir == existItem.IsDir())
212 item.LinkFlag = existItem.LinkFlag;
213
214 item.SparseBlocks = existItem.SparseBlocks;
215 item.Size = existItem.Size;
216 item.PackSize = existItem.PackSize;
217 }
218
219 item.DeviceMajorDefined = existItem.DeviceMajorDefined;
220 item.DeviceMinorDefined = existItem.DeviceMinorDefined;
221 item.DeviceMajor = existItem.DeviceMajor;
222 item.DeviceMinor = existItem.DeviceMinor;
223 item.UID = existItem.UID;
224 item.GID = existItem.GID;
225
226 RINOK(outArchive.WriteHeader(item));
227 RINOK(inStream->Seek((Int64)existItem.GetDataPosition(), STREAM_SEEK_SET, NULL));
228 size = existItem.PackSize;
229 }
230 else
231 {
232 RINOK(inStream->Seek((Int64)existItem.HeaderPos, STREAM_SEEK_SET, NULL));
233 size = existItem.GetFullSize();
234 }
235
236 streamSpec->Init(size);
237
238 if (opCallback)
239 {
240 RINOK(opCallback->ReportOperation(
241 NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
242 NUpdateNotifyOp::kReplicate))
243 }
244
245 RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
246 if (copyCoderSpec->TotalSize != size)
247 return E_FAIL;
248 outArchive.Pos += size;
249 RINOK(outArchive.FillDataResidual(existItem.PackSize));
250 complexity += size;
251 }
252 }
253
254 lps->InSize = lps->OutSize = complexity;
255 RINOK(lps->SetCur());
256 return outArchive.WriteFinishHeader();
257}
258
259}}
diff --git a/CPP/7zip/Archive/Tar/TarUpdate.h b/CPP/7zip/Archive/Tar/TarUpdate.h
new file mode 100644
index 0000000..1e3d021
--- /dev/null
+++ b/CPP/7zip/Archive/Tar/TarUpdate.h
@@ -0,0 +1,41 @@
1// TarUpdate.h
2
3#ifndef __TAR_UPDATE_H
4#define __TAR_UPDATE_H
5
6#include "../IArchive.h"
7
8#include "TarItem.h"
9
10namespace NArchive {
11namespace NTar {
12
13struct CUpdateItem
14{
15 int IndexInArc;
16 unsigned IndexInClient;
17 UInt64 Size;
18 Int64 MTime;
19 UInt32 Mode;
20 bool NewData;
21 bool NewProps;
22 bool IsDir;
23 AString Name;
24 AString User;
25 AString Group;
26
27 CUpdateItem(): Size(0), IsDir(false) {}
28};
29
30HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
31 const CObjectVector<CItemEx> &inputItems,
32 const CObjectVector<CUpdateItem> &updateItems,
33 UINT codePage, unsigned utfFlags,
34 IArchiveUpdateCallback *updateCallback);
35
36HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
37 UINT codePage, unsigned utfFlags, bool convertSlash);
38
39}}
40
41#endif