aboutsummaryrefslogtreecommitdiff
path: root/CPP/7zip/Archive/Tar
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CPP/7zip/Archive/Tar/TarHandler.cpp445
-rw-r--r--CPP/7zip/Archive/Tar/TarHandler.h27
-rw-r--r--CPP/7zip/Archive/Tar/TarHandlerOut.cpp209
-rw-r--r--CPP/7zip/Archive/Tar/TarHeader.cpp77
-rw-r--r--CPP/7zip/Archive/Tar/TarHeader.h8
-rw-r--r--CPP/7zip/Archive/Tar/TarIn.cpp945
-rw-r--r--CPP/7zip/Archive/Tar/TarIn.h130
-rw-r--r--CPP/7zip/Archive/Tar/TarItem.h246
-rw-r--r--CPP/7zip/Archive/Tar/TarOut.cpp561
-rw-r--r--CPP/7zip/Archive/Tar/TarOut.h31
-rw-r--r--CPP/7zip/Archive/Tar/TarRegister.cpp16
-rw-r--r--CPP/7zip/Archive/Tar/TarUpdate.cpp406
-rw-r--r--CPP/7zip/Archive/Tar/TarUpdate.h39
13 files changed, 2623 insertions, 517 deletions
diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp
index 2f23dd8..bd04bd7 100644
--- a/CPP/7zip/Archive/Tar/TarHandler.cpp
+++ b/CPP/7zip/Archive/Tar/TarHandler.cpp
@@ -36,22 +36,34 @@ static const Byte kProps[] =
36 kpidSize, 36 kpidSize,
37 kpidPackSize, 37 kpidPackSize,
38 kpidMTime, 38 kpidMTime,
39 kpidCTime,
40 kpidATime,
39 kpidPosixAttrib, 41 kpidPosixAttrib,
40 kpidUser, 42 kpidUser,
41 kpidGroup, 43 kpidGroup,
44 kpidUserId,
45 kpidGroupId,
42 kpidSymLink, 46 kpidSymLink,
43 kpidHardLink, 47 kpidHardLink,
44 kpidCharacts 48 kpidCharacts,
45 // kpidLinkType 49 kpidComment
50 , kpidDeviceMajor
51 , kpidDeviceMinor
52 // , kpidDevice
53 // , kpidHeadersSize // for debug
54 // , kpidOffset // for debug
46}; 55};
47 56
48static const Byte kArcProps[] = 57static const Byte kArcProps[] =
49{ 58{
50 kpidHeadersSize, 59 kpidHeadersSize,
51 kpidCodePage, 60 kpidCodePage,
52 kpidCharacts 61 kpidCharacts,
62 kpidComment
53}; 63};
54 64
65static const char *k_Characts_Prefix = "PREFIX";
66
55IMP_IInArchive_Props 67IMP_IInArchive_Props
56IMP_IInArchive_ArcProps 68IMP_IInArchive_ArcProps
57 69
@@ -60,14 +72,14 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
60 NCOM::CPropVariant prop; 72 NCOM::CPropVariant prop;
61 switch (propID) 73 switch (propID)
62 { 74 {
63 case kpidPhySize: if (_phySizeDefined) prop = _phySize; break; 75 case kpidPhySize: if (_arc._phySize_Defined) prop = _arc._phySize; break;
64 case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break; 76 case kpidHeadersSize: if (_arc._phySize_Defined) prop = _arc._headersSize; break;
65 case kpidErrorFlags: 77 case kpidErrorFlags:
66 { 78 {
67 UInt32 flags = 0; 79 UInt32 flags = 0;
68 if (!_isArc) 80 if (!_isArc)
69 flags |= kpv_ErrorFlags_IsNotArc; 81 flags |= kpv_ErrorFlags_IsNotArc;
70 else switch (_error) 82 else switch (_arc._error)
71 { 83 {
72 case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break; 84 case k_ErrorType_UnexpectedEnd: flags = kpv_ErrorFlags_UnexpectedEnd; break;
73 case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break; 85 case k_ErrorType_Corrupted: flags = kpv_ErrorFlags_HeadersError; break;
@@ -82,7 +94,7 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
82 94
83 case kpidWarningFlags: 95 case kpidWarningFlags:
84 { 96 {
85 if (_warning) 97 if (_arc._is_Warning)
86 prop = kpv_ErrorFlags_HeadersError; 98 prop = kpv_ErrorFlags_HeadersError;
87 break; 99 break;
88 } 100 }
@@ -107,37 +119,38 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
107 119
108 case kpidCharacts: 120 case kpidCharacts:
109 { 121 {
110 prop = _encodingCharacts.GetCharactsString(); 122 AString s;
123 if (_arc._are_Gnu) s.Add_OptSpaced("GNU");
124 if (_arc._are_Posix) s.Add_OptSpaced("POSIX");
125 if (_arc._are_Pax_Items) s.Add_OptSpaced("PAX_ITEM");
126 if (_arc._pathPrefix_WasUsed) s.Add_OptSpaced(k_Characts_Prefix);
127 if (_arc._are_LongName) s.Add_OptSpaced("LongName");
128 if (_arc._are_LongLink) s.Add_OptSpaced("LongLink");
129 if (_arc._are_Pax) s.Add_OptSpaced("PAX");
130 if (_arc._are_pax_path) s.Add_OptSpaced("path");
131 if (_arc._are_pax_link) s.Add_OptSpaced("linkpath");
132 if (_arc._are_mtime) s.Add_OptSpaced("mtime");
133 if (_arc._are_atime) s.Add_OptSpaced("atime");
134 if (_arc._are_ctime) s.Add_OptSpaced("ctime");
135 if (_arc._is_PaxGlobal_Error) s.Add_OptSpaced("PAX_GLOBAL_ERROR");
136 s.Add_OptSpaced(_encodingCharacts.GetCharactsString());
137 prop = s;
111 break; 138 break;
112 } 139 }
113 }
114 prop.Detach(value);
115 return S_OK;
116}
117 140
118HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item) 141 case kpidComment:
119{ 142 {
120 item.HeaderPos = _phySize; 143 if (_arc.PaxGlobal_Defined)
121 EErrorType error; 144 {
122 HRESULT res = ReadItem(stream, filled, item, error); 145 AString s;
123 if (error == k_ErrorType_Warning) 146 _arc.PaxGlobal.Print_To_String(s);
124 _warning = true; 147 if (!s.IsEmpty())
125 else if (error != k_ErrorType_OK) 148 prop = s;
126 _error = error; 149 }
127 RINOK(res); 150 break;
128 if (filled) 151 }
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 } 152 }
139 _phySize += item.HeaderSize; 153 prop.Detach(value);
140 _headersSize += item.HeaderSize;
141 return S_OK; 154 return S_OK;
142} 155}
143 156
@@ -199,16 +212,20 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
199 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); 212 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
200 } 213 }
201 214
202 _phySizeDefined = true; 215 _arc._phySize_Defined = true;
203 216
204 // bool utf8_OK = true; 217 // bool utf8_OK = true;
205 218
219 _arc.SeqStream = stream;
220 _arc.InStream = stream;
221 _arc.OpenCallback = callback;
222
223 CItemEx item;
206 for (;;) 224 for (;;)
207 { 225 {
208 CItemEx item; 226 _arc.NumFiles = _items.Size();
209 bool filled; 227 RINOK(_arc.ReadItem(item));
210 RINOK(ReadItem2(stream, filled, item)); 228 if (!_arc.filled)
211 if (!filled)
212 break; 229 break;
213 230
214 _isArc = true; 231 _isArc = true;
@@ -228,10 +245,10 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
228 245
229 _items.Add(item); 246 _items.Add(item);
230 247
231 RINOK(stream->Seek((Int64)item.GetPackSizeAligned(), STREAM_SEEK_CUR, &_phySize)); 248 RINOK(stream->Seek((Int64)item.Get_PackSize_Aligned(), STREAM_SEEK_CUR, &_arc._phySize));
232 if (_phySize > endPos) 249 if (_arc._phySize > endPos)
233 { 250 {
234 _error = k_ErrorType_UnexpectedEnd; 251 _arc._error = k_ErrorType_UnexpectedEnd;
235 break; 252 break;
236 } 253 }
237 /* 254 /*
@@ -241,6 +258,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
241 break; 258 break;
242 } 259 }
243 */ 260 */
261 /*
244 if (callback) 262 if (callback)
245 { 263 {
246 if (_items.Size() == 1) 264 if (_items.Size() == 1)
@@ -249,10 +267,11 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
249 } 267 }
250 if ((_items.Size() & 0x3FF) == 0) 268 if ((_items.Size() & 0x3FF) == 0)
251 { 269 {
252 UInt64 numFiles = _items.Size(); 270 const UInt64 numFiles = _items.Size();
253 RINOK(callback->SetCompleted(&numFiles, &_phySize)); 271 RINOK(callback->SetCompleted(&numFiles, &_phySize));
254 } 272 }
255 } 273 }
274 */
256 } 275 }
257 276
258 /* 277 /*
@@ -266,7 +285,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
266 285
267 if (_items.Size() == 0) 286 if (_items.Size() == 0)
268 { 287 {
269 if (_error != k_ErrorType_OK) 288 if (_arc._error != k_ErrorType_OK)
270 { 289 {
271 _isArc = false; 290 _isArc = false;
272 return S_FALSE; 291 return S_FALSE;
@@ -294,6 +313,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
294STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback) 313STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback)
295{ 314{
296 COM_TRY_BEGIN 315 COM_TRY_BEGIN
316 // for (int i = 0; i < 10; i++) // for debug
297 { 317 {
298 Close(); 318 Close();
299 RINOK(Open2(stream, openArchiveCallback)); 319 RINOK(Open2(stream, openArchiveCallback));
@@ -314,16 +334,11 @@ STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
314STDMETHODIMP CHandler::Close() 334STDMETHODIMP CHandler::Close()
315{ 335{
316 _isArc = false; 336 _isArc = false;
317 _warning = false;
318 _error = k_ErrorType_OK;
319 337
320 _phySizeDefined = false; 338 _arc.Clear();
321 _phySize = 0; 339
322 _headersSize = 0;
323 _curIndex = 0; 340 _curIndex = 0;
324 _latestIsRead = false; 341 _latestIsRead = false;
325 // _isSparse = false;
326 _thereIsPaxExtendedHeader = false;
327 _encodingCharacts.Clear(); 342 _encodingCharacts.Clear();
328 _items.Clear(); 343 _items.Clear();
329 _seqStream.Release(); 344 _seqStream.Release();
@@ -351,12 +366,12 @@ HRESULT CHandler::SkipTo(UInt32 index)
351 { 366 {
352 if (_latestIsRead) 367 if (_latestIsRead)
353 { 368 {
354 UInt64 packSize = _latestItem.GetPackSizeAligned(); 369 const UInt64 packSize = _latestItem.Get_PackSize_Aligned();
355 RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL)); 370 RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
356 _phySize += copyCoderSpec->TotalSize; 371 _arc._phySize += copyCoderSpec->TotalSize;
357 if (copyCoderSpec->TotalSize != packSize) 372 if (copyCoderSpec->TotalSize != packSize)
358 { 373 {
359 _error = k_ErrorType_UnexpectedEnd; 374 _arc._error = k_ErrorType_UnexpectedEnd;
360 return S_FALSE; 375 return S_FALSE;
361 } 376 }
362 _latestIsRead = false; 377 _latestIsRead = false;
@@ -364,11 +379,12 @@ HRESULT CHandler::SkipTo(UInt32 index)
364 } 379 }
365 else 380 else
366 { 381 {
367 bool filled; 382 _arc.SeqStream = _seqStream;
368 RINOK(ReadItem2(_seqStream, filled, _latestItem)); 383 _arc.InStream = NULL;
369 if (!filled) 384 RINOK(_arc.ReadItem(_latestItem));
385 if (!_arc.filled)
370 { 386 {
371 _phySizeDefined = true; 387 _arc._phySize_Defined = true;
372 return E_INVALIDARG; 388 return E_INVALIDARG;
373 } 389 }
374 _latestIsRead = true; 390 _latestIsRead = true;
@@ -390,6 +406,69 @@ void CHandler::TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant
390 prop = dest; 406 prop = dest;
391} 407}
392 408
409
410static void PaxTimeToProp(const CPaxTime &pt, NWindows::NCOM::CPropVariant &prop)
411{
412 UInt64 v;
413 if (!NTime::UnixTime64_To_FileTime64(pt.Sec, v))
414 return;
415 if (pt.Ns != 0)
416 v += pt.Ns / 100;
417 FILETIME ft;
418 ft.dwLowDateTime = (DWORD)v;
419 ft.dwHighDateTime = (DWORD)(v >> 32);
420 prop.SetAsTimeFrom_FT_Prec_Ns100(ft,
421 k_PropVar_TimePrec_Base + pt.NumDigits, pt.Ns % 100);
422}
423
424
425#define ValToHex(t) ((char)(((t) < 10) ? ('0' + (t)) : ('a' + ((t) - 10))))
426
427static void AddByteToHex2(unsigned val, AString &s)
428{
429 unsigned t;
430 t = val >> 4;
431 s += ValToHex(t);
432 t = val & 0xF;
433 s += ValToHex(t);
434}
435
436static void AddSpecCharToString(const char c, AString &s)
437{
438 if ((Byte)c <= 0x20 || (Byte)c > 127)
439 {
440 s += '[';
441 AddByteToHex2((Byte)(c), s);
442 s += ']';
443 }
444 else
445 s += c;
446}
447
448static void AddSpecUInt64(AString &s, const char *name, UInt64 v)
449{
450 if (v != 0)
451 {
452 s.Add_OptSpaced(name);
453 if (v > 1)
454 {
455 s += ':';
456 s.Add_UInt64(v);
457 }
458 }
459}
460
461static void AddSpecBools(AString &s, const char *name, bool b1, bool b2)
462{
463 if (b1)
464 {
465 s.Add_OptSpaced(name);
466 if (b2)
467 s += '*';
468 }
469}
470
471
393STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) 472STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
394{ 473{
395 COM_TRY_BEGIN 474 COM_TRY_BEGIN
@@ -413,49 +492,189 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
413 { 492 {
414 case kpidPath: TarStringToUnicode(item->Name, prop, true); break; 493 case kpidPath: TarStringToUnicode(item->Name, prop, true); break;
415 case kpidIsDir: prop = item->IsDir(); break; 494 case kpidIsDir: prop = item->IsDir(); break;
416 case kpidSize: prop = item->GetUnpackSize(); break; 495 case kpidSize: prop = item->Get_UnpackSize(); break;
417 case kpidPackSize: prop = item->GetPackSizeAligned(); break; 496 case kpidPackSize: prop = item->Get_PackSize_Aligned(); break;
418 case kpidMTime: 497 case kpidMTime:
419 if (item->MTime != 0) 498 {
499 /*
500 // for debug:
501 PropVariant_SetFrom_UnixTime(prop, 1 << 30);
502 prop.wReserved1 = k_PropVar_TimePrec_Base + 1;
503 prop.wReserved2 = 12;
504 break;
505 */
506
507 if (item->PaxTimes.MTime.IsDefined())
508 PaxTimeToProp(item->PaxTimes.MTime, prop);
509 else
510 // if (item->MTime != 0)
420 { 511 {
512 // we allow (item->MTime == 0)
421 FILETIME ft; 513 FILETIME ft;
422 if (NTime::UnixTime64ToFileTime(item->MTime, ft)) 514 if (NTime::UnixTime64_To_FileTime(item->MTime, ft))
423 prop = ft; 515 {
516 unsigned prec = k_PropVar_TimePrec_Unix;
517 if (item->MTime_IsBin)
518 {
519 /* we report here that it's Int64-UnixTime
520 instead of basic UInt32-UnixTime range */
521 prec = k_PropVar_TimePrec_Base;
522 }
523 prop.SetAsTimeFrom_FT_Prec(ft, prec);
524 }
424 } 525 }
425 break; 526 break;
527 }
528 case kpidATime:
529 if (item->PaxTimes.ATime.IsDefined())
530 PaxTimeToProp(item->PaxTimes.ATime, prop);
531 break;
532 case kpidCTime:
533 if (item->PaxTimes.CTime.IsDefined())
534 PaxTimeToProp(item->PaxTimes.CTime, prop);
535 break;
426 case kpidPosixAttrib: prop = item->Get_Combined_Mode(); break; 536 case kpidPosixAttrib: prop = item->Get_Combined_Mode(); break;
427 case kpidUser: TarStringToUnicode(item->User, prop); break; 537
428 case kpidGroup: TarStringToUnicode(item->Group, prop); break; 538 case kpidUser:
429 case kpidSymLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kSymLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break; 539 if (!item->User.IsEmpty())
430 case kpidHardLink: if (item->LinkFlag == NFileHeader::NLinkFlag::kHardLink && !item->LinkName.IsEmpty()) TarStringToUnicode(item->LinkName, prop); break; 540 TarStringToUnicode(item->User, prop);
431 // case kpidLinkType: prop = (int)item->LinkFlag; break; 541 break;
542 case kpidGroup:
543 if (!item->Group.IsEmpty())
544 TarStringToUnicode(item->Group, prop);
545 break;
546
547 case kpidUserId:
548 // if (item->UID != 0)
549 prop = (UInt32)item->UID;
550 break;
551 case kpidGroupId:
552 // if (item->GID != 0)
553 prop = (UInt32)item->GID;
554 break;
555
556 case kpidDeviceMajor:
557 if (item->DeviceMajor_Defined)
558 // if (item->DeviceMajor != 0)
559 prop = (UInt32)item->DeviceMajor;
560 break;
561
562 case kpidDeviceMinor:
563 if (item->DeviceMinor_Defined)
564 // if (item->DeviceMinor != 0)
565 prop = (UInt32)item->DeviceMinor;
566 break;
567 /*
568 case kpidDevice:
569 if (item->DeviceMajor_Defined)
570 if (item->DeviceMinor_Defined)
571 prop = (UInt64)MY_dev_makedev(item->DeviceMajor, item->DeviceMinor);
572 break;
573 */
574
575 case kpidSymLink:
576 if (item->Is_SymLink())
577 if (!item->LinkName.IsEmpty())
578 TarStringToUnicode(item->LinkName, prop);
579 break;
580 case kpidHardLink:
581 if (item->Is_HardLink())
582 if (!item->LinkName.IsEmpty())
583 TarStringToUnicode(item->LinkName, prop);
584 break;
585
432 case kpidCharacts: 586 case kpidCharacts:
433 { 587 {
434 AString s = item->EncodingCharacts.GetCharactsString(); 588 AString s;
435 if (item->IsThereWarning())
436 { 589 {
437 s.Add_Space_if_NotEmpty(); 590 s.Add_Space_if_NotEmpty();
438 s += "HEADER_ERROR"; 591 AddSpecCharToString(item->LinkFlag, s);
439 } 592 }
440 prop = s; 593 if (item->IsMagic_GNU())
594 s.Add_OptSpaced("GNU");
595 else if (item->IsMagic_Posix_ustar_00())
596 s.Add_OptSpaced("POSIX");
597 else
598 {
599 s.Add_Space_if_NotEmpty();
600 for (unsigned i = 0; i < sizeof(item->Magic); i++)
601 AddSpecCharToString(item->Magic[i], s);
602 }
603
604 if (item->IsSignedChecksum)
605 s.Add_OptSpaced("SignedChecksum");
606
607 if (item->Prefix_WasUsed)
608 s.Add_OptSpaced(k_Characts_Prefix);
609
610 s.Add_OptSpaced(item->EncodingCharacts.GetCharactsString());
611
612 // AddSpecUInt64(s, "LongName", item->Num_LongName_Records);
613 // AddSpecUInt64(s, "LongLink", item->Num_LongLink_Records);
614 AddSpecBools(s, "LongName", item->LongName_WasUsed, item->LongName_WasUsed_2);
615 AddSpecBools(s, "LongLink", item->LongLink_WasUsed, item->LongLink_WasUsed_2);
616
617 if (item->MTime_IsBin)
618 s.Add_OptSpaced("bin_mtime");
619 if (item->PackSize_IsBin)
620 s.Add_OptSpaced("bin_psize");
621 if (item->Size_IsBin)
622 s.Add_OptSpaced("bin_size");
623
624 AddSpecUInt64(s, "PAX", item->Num_Pax_Records);
625
626 if (item->PaxTimes.MTime.IsDefined()) s.Add_OptSpaced("mtime");
627 if (item->PaxTimes.ATime.IsDefined()) s.Add_OptSpaced("atime");
628 if (item->PaxTimes.CTime.IsDefined()) s.Add_OptSpaced("ctime");
629
630 if (item->pax_path_WasUsed)
631 s.Add_OptSpaced("pax_path");
632 if (item->pax_link_WasUsed)
633 s.Add_OptSpaced("pax_linkpath");
634 if (item->pax_size_WasUsed)
635 s.Add_OptSpaced("pax_size");
636
637 if (item->IsThereWarning())
638 s.Add_OptSpaced("WARNING");
639 if (item->HeaderError)
640 s.Add_OptSpaced("ERROR");
641 if (item->Pax_Error)
642 s.Add_OptSpaced("PAX_error");
643 if (!item->PaxExtra.RawLines.IsEmpty())
644 s.Add_OptSpaced("PAX_unsupported_line");
645 if (item->Pax_Overflow)
646 s.Add_OptSpaced("PAX_overflow");
647 if (!s.IsEmpty())
648 prop = s;
441 break; 649 break;
442 } 650 }
651 case kpidComment:
652 {
653 AString s;
654 item->PaxExtra.Print_To_String(s);
655 if (!s.IsEmpty())
656 prop = s;
657 break;
658 }
659 // case kpidHeadersSize: prop = item->HeaderSize; break; // for debug
660 // case kpidOffset: prop = item->HeaderPos; break; // for debug
443 } 661 }
444 prop.Detach(value); 662 prop.Detach(value);
445 return S_OK; 663 return S_OK;
446 COM_TRY_END 664 COM_TRY_END
447} 665}
448 666
667
449HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, 668HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
450 Int32 testMode, IArchiveExtractCallback *extractCallback) 669 Int32 testMode, IArchiveExtractCallback *extractCallback)
451{ 670{
452 COM_TRY_BEGIN 671 COM_TRY_BEGIN
453 ISequentialInStream *stream = _seqStream; 672 ISequentialInStream *stream = _seqStream;
454 bool seqMode = (_stream == NULL); 673 const bool seqMode = (_stream == NULL);
455 if (!seqMode) 674 if (!seqMode)
456 stream = _stream; 675 stream = _stream;
457 676
458 bool allFilesMode = (numItems == (UInt32)(Int32)-1); 677 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
459 if (allFilesMode) 678 if (allFilesMode)
460 numItems = _items.Size(); 679 numItems = _items.Size();
461 if (_stream && numItems == 0) 680 if (_stream && numItems == 0)
@@ -463,7 +682,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
463 UInt64 totalSize = 0; 682 UInt64 totalSize = 0;
464 UInt32 i; 683 UInt32 i;
465 for (i = 0; i < numItems; i++) 684 for (i = 0; i < numItems; i++)
466 totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize(); 685 totalSize += _items[allFilesMode ? i : indices[i]].Get_UnpackSize();
467 extractCallback->SetTotal(totalSize); 686 extractCallback->SetTotal(totalSize);
468 687
469 UInt64 totalPackSize; 688 UInt64 totalPackSize;
@@ -503,9 +722,9 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
503 item = &_items[index]; 722 item = &_items[index];
504 723
505 RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); 724 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
506 UInt64 unpackSize = item->GetUnpackSize(); 725 const UInt64 unpackSize = item->Get_UnpackSize();
507 totalSize += unpackSize; 726 totalSize += unpackSize;
508 totalPackSize += item->GetPackSizeAligned(); 727 totalPackSize += item->Get_PackSize_Aligned();
509 if (item->IsDir()) 728 if (item->IsDir())
510 { 729 {
511 RINOK(extractCallback->PrepareOperation(askMode)); 730 RINOK(extractCallback->PrepareOperation(askMode));
@@ -539,7 +758,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
539 758
540 Int32 opRes = NExtract::NOperationResult::kOK; 759 Int32 opRes = NExtract::NOperationResult::kOK;
541 CMyComPtr<ISequentialInStream> inStream2; 760 CMyComPtr<ISequentialInStream> inStream2;
542 if (!item->IsSparse()) 761 if (!item->Is_Sparse())
543 inStream2 = inStream; 762 inStream2 = inStream;
544 else 763 else
545 { 764 {
@@ -549,7 +768,7 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
549 } 768 }
550 769
551 { 770 {
552 if (item->IsSymLink()) 771 if (item->Is_SymLink())
553 { 772 {
554 RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len())); 773 RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Len()));
555 } 774 }
@@ -557,9 +776,9 @@ HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
557 { 776 {
558 if (!seqMode) 777 if (!seqMode)
559 { 778 {
560 RINOK(_stream->Seek((Int64)item->GetDataPosition(), STREAM_SEEK_SET, NULL)); 779 RINOK(_stream->Seek((Int64)item->Get_DataPos(), STREAM_SEEK_SET, NULL));
561 } 780 }
562 streamSpec->Init(item->GetPackSizeAligned()); 781 streamSpec->Init(item->Get_PackSize_Aligned());
563 RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress)); 782 RINOK(copyCoder->Code(inStream2, outStream, NULL, NULL, progress));
564 } 783 }
565 if (outStreamSpec->GetRem() != 0) 784 if (outStreamSpec->GetRem() != 0)
@@ -628,7 +847,7 @@ STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize)
628 unsigned left = 0, right = item.SparseBlocks.Size(); 847 unsigned left = 0, right = item.SparseBlocks.Size();
629 for (;;) 848 for (;;)
630 { 849 {
631 unsigned mid = (left + right) / 2; 850 const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
632 if (mid == left) 851 if (mid == left)
633 break; 852 break;
634 if (_virtPos < item.SparseBlocks[mid].Offset) 853 if (_virtPos < item.SparseBlocks[mid].Offset)
@@ -648,7 +867,7 @@ STDMETHODIMP CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize)
648 UInt64 phyPos = PhyOffsets[left] + relat; 867 UInt64 phyPos = PhyOffsets[left] + relat;
649 if (_needStartSeek || _phyPos != phyPos) 868 if (_needStartSeek || _phyPos != phyPos)
650 { 869 {
651 RINOK(Handler->_stream->Seek((Int64)(item.GetDataPosition() + phyPos), STREAM_SEEK_SET, NULL)); 870 RINOK(Handler->_stream->Seek((Int64)(item.Get_DataPos() + phyPos), STREAM_SEEK_SET, NULL));
652 _needStartSeek = false; 871 _needStartSeek = false;
653 _phyPos = phyPos; 872 _phyPos = phyPos;
654 } 873 }
@@ -698,7 +917,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
698 917
699 const CItemEx &item = _items[index]; 918 const CItemEx &item = _items[index];
700 919
701 if (item.IsSparse()) 920 if (item.Is_Sparse())
702 { 921 {
703 CSparseStream *streamSpec = new CSparseStream; 922 CSparseStream *streamSpec = new CSparseStream;
704 CMyComPtr<IInStream> streamTemp = streamSpec; 923 CMyComPtr<IInStream> streamTemp = streamSpec;
@@ -718,24 +937,30 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
718 return S_OK; 937 return S_OK;
719 } 938 }
720 939
721 if (item.IsSymLink()) 940 if (item.Is_SymLink())
722 { 941 {
723 Create_BufInStream_WithReference((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this, stream); 942 Create_BufInStream_WithReference((const Byte *)(const char *)item.LinkName, item.LinkName.Len(), (IInArchive *)this, stream);
724 return S_OK; 943 return S_OK;
725 } 944 }
726 945
727 return CreateLimitedInStream(_stream, item.GetDataPosition(), item.PackSize, stream); 946 return CreateLimitedInStream(_stream, item.Get_DataPos(), item.PackSize, stream);
728 947
729 COM_TRY_END 948 COM_TRY_END
730} 949}
731 950
951
732void CHandler::Init() 952void CHandler::Init()
733{ 953{
734 _forceCodePage = false; 954 _forceCodePage = false;
735 _curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP; 955 _curCodePage = _specifiedCodePage = CP_UTF8; // CP_OEMCP;
736 _thereIsPaxExtendedHeader = false; 956 _posixMode = false;
957 _posixMode_WasForced = false;
958 // TimeOptions.Clear();
959 _handlerTimeOptions.Init();
960 // _handlerTimeOptions.Write_MTime.Val = true; // it's default already
737} 961}
738 962
963
739STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps) 964STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
740{ 965{
741 Init(); 966 Init();
@@ -768,8 +993,54 @@ STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVAR
768 else if (name.IsPrefixedBy_Ascii_NoCase("memuse")) 993 else if (name.IsPrefixedBy_Ascii_NoCase("memuse"))
769 { 994 {
770 } 995 }
996 else if (name.IsEqualTo("m"))
997 {
998 if (prop.vt != VT_BSTR)
999 return E_INVALIDARG;
1000 const UString s = prop.bstrVal;
1001 if (s.IsEqualTo_Ascii_NoCase("pax") ||
1002 s.IsEqualTo_Ascii_NoCase("posix"))
1003 _posixMode = true;
1004 else if (s.IsEqualTo_Ascii_NoCase("gnu"))
1005 _posixMode = false;
1006 else
1007 return E_INVALIDARG;
1008 _posixMode_WasForced = true;
1009 }
771 else 1010 else
1011 {
1012 /*
1013 if (name.IsPrefixedBy_Ascii_NoCase("td"))
1014 {
1015 name.Delete(0, 3);
1016 if (prop.vt == VT_EMPTY)
1017 {
1018 if (name.IsEqualTo_Ascii_NoCase("n"))
1019 {
1020 // TimeOptions.UseNativeDigits = true;
1021 }
1022 else if (name.IsEqualTo_Ascii_NoCase("r"))
1023 {
1024 // TimeOptions.RemoveZeroDigits = true;
1025 }
1026 else
1027 return E_INVALIDARG;
1028 }
1029 else
1030 {
1031 UInt32 numTimeDigits = 0;
1032 RINOK(ParsePropToUInt32(name, prop, numTimeDigits));
1033 TimeOptions.NumDigits_WasForced = true;
1034 TimeOptions.NumDigits = numTimeDigits;
1035 }
1036 }
1037 */
1038 bool processed = false;
1039 RINOK(_handlerTimeOptions.Parse(name, prop, processed));
1040 if (processed)
1041 continue;
772 return E_INVALIDARG; 1042 return E_INVALIDARG;
1043 }
773 } 1044 }
774 return S_OK; 1045 return S_OK;
775} 1046}
diff --git a/CPP/7zip/Archive/Tar/TarHandler.h b/CPP/7zip/Archive/Tar/TarHandler.h
index 4834c2a..44a9980 100644
--- a/CPP/7zip/Archive/Tar/TarHandler.h
+++ b/CPP/7zip/Archive/Tar/TarHandler.h
@@ -9,7 +9,7 @@
9 9
10#include "../../Compress/CopyCoder.h" 10#include "../../Compress/CopyCoder.h"
11 11
12#include "../IArchive.h" 12#include "../Common/HandlerOut.h"
13 13
14#include "TarIn.h" 14#include "TarIn.h"
15 15
@@ -29,31 +29,26 @@ public:
29 CMyComPtr<IInStream> _stream; 29 CMyComPtr<IInStream> _stream;
30 CMyComPtr<ISequentialInStream> _seqStream; 30 CMyComPtr<ISequentialInStream> _seqStream;
31private: 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; 32 bool _isArc;
42 33 bool _posixMode_WasForced;
43 // bool _isSparse; 34 bool _posixMode;
44 bool _thereIsPaxExtendedHeader;
45
46 bool _forceCodePage; 35 bool _forceCodePage;
47 UInt32 _specifiedCodePage; 36 UInt32 _specifiedCodePage;
48 UInt32 _curCodePage; 37 UInt32 _curCodePage;
49 UInt32 _openCodePage; 38 UInt32 _openCodePage;
50 39 // CTimeOptions TimeOptions;
40 CHandlerTimeOptions _handlerTimeOptions;
51 CEncodingCharacts _encodingCharacts; 41 CEncodingCharacts _encodingCharacts;
52 42
43 UInt32 _curIndex;
44 bool _latestIsRead;
45 CItemEx _latestItem;
46
47 CArchive _arc;
48
53 NCompress::CCopyCoder *copyCoderSpec; 49 NCompress::CCopyCoder *copyCoderSpec;
54 CMyComPtr<ICompressCoder> copyCoder; 50 CMyComPtr<ICompressCoder> copyCoder;
55 51
56 HRESULT ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo);
57 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); 52 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
58 HRESULT SkipTo(UInt32 index); 53 HRESULT SkipTo(UInt32 index);
59 void TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs = false) const; 54 void TarStringToUnicode(const AString &s, NWindows::NCOM::CPropVariant &prop, bool toOs = false) const;
diff --git a/CPP/7zip/Archive/Tar/TarHandlerOut.cpp b/CPP/7zip/Archive/Tar/TarHandlerOut.cpp
index 5ddb4b2..53255e4 100644
--- a/CPP/7zip/Archive/Tar/TarHandlerOut.cpp
+++ b/CPP/7zip/Archive/Tar/TarHandlerOut.cpp
@@ -2,15 +2,16 @@
2 2
3#include "StdAfx.h" 3#include "StdAfx.h"
4 4
5// #include <stdio.h>
6
5#include "../../../Common/ComTry.h" 7#include "../../../Common/ComTry.h"
6#include "../../../Common/Defs.h"
7#include "../../../Common/MyLinux.h" 8#include "../../../Common/MyLinux.h"
8#include "../../../Common/StringConvert.h" 9#include "../../../Common/StringConvert.h"
9#include "../../../Common/UTFConvert.h"
10 10
11#include "../../../Windows/PropVariant.h"
12#include "../../../Windows/TimeUtils.h" 11#include "../../../Windows/TimeUtils.h"
13 12
13#include "../Common/ItemNameUtils.h"
14
14#include "TarHandler.h" 15#include "TarHandler.h"
15#include "TarUpdate.h" 16#include "TarUpdate.h"
16 17
@@ -21,10 +22,35 @@ namespace NTar {
21 22
22STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) 23STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
23{ 24{
24 *type = NFileTimeType::kUnix; 25 UInt32 t = NFileTimeType::kUnix;
26 const UInt32 prec = _handlerTimeOptions.Prec;
27 if (prec != (UInt32)(Int32)-1)
28 {
29 t = NFileTimeType::kWindows;
30 if (prec == k_PropVar_TimePrec_0 ||
31 prec == k_PropVar_TimePrec_100ns)
32 t = NFileTimeType::kWindows;
33 else if (prec == k_PropVar_TimePrec_HighPrec)
34 t = k_PropVar_TimePrec_1ns;
35 else if (prec >= k_PropVar_TimePrec_Base)
36 t = prec;
37 }
38 // 7-Zip before 22.00 fails, if unknown typeType.
39 *type = t;
25 return S_OK; 40 return S_OK;
26} 41}
27 42
43
44void Get_AString_From_UString(const UString &s, AString &res,
45 UINT codePage, unsigned utfFlags)
46{
47 if (codePage == CP_UTF8)
48 ConvertUnicodeToUTF8_Flags(s, res, utfFlags);
49 else
50 UnicodeStringToMultiByte2(res, s, codePage);
51}
52
53
28HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res, 54HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
29 UINT codePage, unsigned utfFlags, bool convertSlash) 55 UINT codePage, unsigned utfFlags, bool convertSlash)
30{ 56{
@@ -36,14 +62,7 @@ HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID pro
36 UString s = prop.bstrVal; 62 UString s = prop.bstrVal;
37 if (convertSlash) 63 if (convertSlash)
38 NItemName::ReplaceSlashes_OsToUnix(s); 64 NItemName::ReplaceSlashes_OsToUnix(s);
39 65 Get_AString_From_UString(s, res, codePage, utfFlags);
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 } 66 }
48 else if (prop.vt != VT_EMPTY) 67 else if (prop.vt != VT_EMPTY)
49 return E_INVALIDARG; 68 return E_INVALIDARG;
@@ -70,12 +89,106 @@ static int CompareUpdateItems(void *const *p1, void *const *p2, void *)
70} 89}
71 90
72 91
92static HRESULT GetTime(UInt32 i, UInt32 pid, IArchiveUpdateCallback *callback,
93 CPaxTime &pt)
94{
95 pt.Clear();
96 NCOM::CPropVariant prop;
97 RINOK(callback->GetProperty(i, pid, &prop));
98 return Prop_To_PaxTime(prop, pt);
99}
100
101
102/*
103static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
104 UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined)
105{
106 NWindows::NCOM::CPropVariant prop;
107 RINOK(callback->GetProperty(i, kpidDevice, &prop));
108 if (prop.vt == VT_EMPTY)
109 return S_OK;
110 if (prop.vt != VT_UI8)
111 return E_INVALIDARG;
112 {
113 const UInt64 v = prop.uhVal.QuadPart;
114 majo = MY_dev_major(v);
115 mino = MY_dev_minor(v);
116 majo_defined = true;
117 mino_defined = true;
118 }
119 return S_OK;
120}
121*/
122
123static HRESULT GetDevice(IArchiveUpdateCallback *callback, UInt32 i,
124 UInt32 pid, UInt32 &id, bool &defined)
125{
126 defined = false;
127 NWindows::NCOM::CPropVariant prop;
128 RINOK(callback->GetProperty(i, pid, &prop));
129 if (prop.vt == VT_EMPTY)
130 return S_OK;
131 if (prop.vt == VT_UI4)
132 {
133 id = prop.ulVal;
134 defined = true;
135 return S_OK;
136 }
137 return E_INVALIDARG;
138}
139
140
141static HRESULT GetUser(IArchiveUpdateCallback *callback, UInt32 i,
142 UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id,
143 UINT codePage, unsigned utfFlags)
144{
145 // printf("\ncallback->GetProperty(i, pidId, &prop))\n");
146
147 bool isSet = false;
148 {
149 NWindows::NCOM::CPropVariant prop;
150 RINOK(callback->GetProperty(i, pidId, &prop));
151 if (prop.vt == VT_UI4)
152 {
153 isSet = true;
154 id = prop.ulVal;
155 // printf("\ncallback->GetProperty(i, pidId, &prop)); = %d \n", (unsigned)id);
156 name.Empty();
157 }
158 else if (prop.vt != VT_EMPTY)
159 return E_INVALIDARG;
160 }
161 {
162 NWindows::NCOM::CPropVariant prop;
163 RINOK(callback->GetProperty(i, pidName, &prop));
164 if (prop.vt == VT_BSTR)
165 {
166 const UString s = prop.bstrVal;
167 Get_AString_From_UString(s, name, codePage, utfFlags);
168 if (!isSet)
169 id = 0;
170 }
171 else if (prop.vt == VT_UI4)
172 {
173 id = prop.ulVal;
174 name.Empty();
175 }
176 else if (prop.vt != VT_EMPTY)
177 return E_INVALIDARG;
178 }
179 return S_OK;
180}
181
182
183
73STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, 184STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
74 IArchiveUpdateCallback *callback) 185 IArchiveUpdateCallback *callback)
75{ 186{
76 COM_TRY_BEGIN 187 COM_TRY_BEGIN
77 188
78 if ((_stream && (_error != k_ErrorType_OK || _warning /* || _isSparse */)) || _seqStream) 189 if ((_stream && (_arc._error != k_ErrorType_OK || _arc._is_Warning
190 /* || _isSparse */
191 )) || _seqStream)
79 return E_NOTIMPL; 192 return E_NOTIMPL;
80 CObjectVector<CUpdateItem> updateItems; 193 CObjectVector<CUpdateItem> updateItems;
81 const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage); 194 const UINT codePage = (_forceCodePage ? _specifiedCodePage : _openCodePage);
@@ -131,25 +244,30 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt
131 else 244 else
132 ui.Mode = prop.ulVal; 245 ui.Mode = prop.ulVal;
133 // 21.07 : we clear high file type bits as GNU TAR. 246 // 21.07 : we clear high file type bits as GNU TAR.
134 ui.Mode &= ~(UInt32)MY_LIN_S_IFMT; 247 // we will clear it later
248 // ui.Mode &= ~(UInt32)MY_LIN_S_IFMT;
135 } 249 }
136 250
137 { 251 if (_handlerTimeOptions.Write_MTime.Val)
138 NCOM::CPropVariant prop; 252 RINOK(GetTime(i, kpidMTime, callback, ui.PaxTimes.MTime))
139 RINOK(callback->GetProperty(i, kpidMTime, &prop)); 253 if (_handlerTimeOptions.Write_ATime.Val)
140 if (prop.vt == VT_EMPTY) 254 RINOK(GetTime(i, kpidATime, callback, ui.PaxTimes.ATime))
141 ui.MTime = 0; 255 if (_handlerTimeOptions.Write_CTime.Val)
142 else if (prop.vt != VT_FILETIME) 256 RINOK(GetTime(i, kpidCTime, callback, ui.PaxTimes.CTime))
143 return E_INVALIDARG; 257
144 else
145 ui.MTime = NTime::FileTimeToUnixTime64(prop.filetime);
146 }
147
148 RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true)); 258 RINOK(GetPropString(callback, i, kpidPath, ui.Name, codePage, utfFlags, true));
149 if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/') 259 if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
150 ui.Name += '/'; 260 ui.Name += '/';
151 RINOK(GetPropString(callback, i, kpidUser, ui.User, codePage, utfFlags, false)); 261 // ui.Name += '/'; // for debug
152 RINOK(GetPropString(callback, i, kpidGroup, ui.Group, codePage, utfFlags, false)); 262
263 if (_posixMode)
264 {
265 RINOK(GetDevice(callback, i, kpidDeviceMajor, ui.DeviceMajor, ui.DeviceMajor_Defined));
266 RINOK(GetDevice(callback, i, kpidDeviceMinor, ui.DeviceMinor, ui.DeviceMinor_Defined));
267 }
268
269 RINOK(GetUser(callback, i, kpidUser, kpidUserId, ui.User, ui.UID, codePage, utfFlags));
270 RINOK(GetUser(callback, i, kpidGroup, kpidGroupId, ui.Group, ui.GID, codePage, utfFlags));
153 } 271 }
154 272
155 if (IntToBool(newData)) 273 if (IntToBool(newData))
@@ -169,13 +287,44 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt
169 updateItems.Add(ui); 287 updateItems.Add(ui);
170 } 288 }
171 289
172 if (_thereIsPaxExtendedHeader) 290 if (_arc._are_Pax_Items)
173 { 291 {
174 // we restore original order of files, if there is pax header block 292 // we restore original order of files, if there are pax items
175 updateItems.Sort(CompareUpdateItems, NULL); 293 updateItems.Sort(CompareUpdateItems, NULL);
176 } 294 }
295
296 CUpdateOptions options;
297
298 options.CodePage = codePage;
299 options.UtfFlags = utfFlags;
300 options.PosixMode = _posixMode;
301
302 options.Write_MTime = _handlerTimeOptions.Write_MTime;
303 options.Write_ATime = _handlerTimeOptions.Write_ATime;
304 options.Write_CTime = _handlerTimeOptions.Write_CTime;
177 305
178 return UpdateArchive(_stream, outStream, _items, updateItems, codePage, utfFlags, callback); 306 // options.TimeOptions = TimeOptions;
307
308 const UInt32 prec = _handlerTimeOptions.Prec;
309 if (prec != (UInt32)(Int32)-1)
310 {
311 unsigned numDigits = 0;
312 if (prec == 0)
313 numDigits = 7;
314 else if (prec == k_PropVar_TimePrec_HighPrec
315 || prec >= k_PropVar_TimePrec_1ns)
316 numDigits = 9;
317 else if (prec >= k_PropVar_TimePrec_Base)
318 numDigits = prec - k_PropVar_TimePrec_Base;
319 options.TimeOptions.NumDigitsMax = numDigits;
320 // options.TimeOptions.RemoveZeroMode =
321 // k_PaxTimeMode_DontRemoveZero; // pure for debug
322 // k_PaxTimeMode_RemoveZero_if_PureSecondOnly; // optimized code
323 // k_PaxTimeMode_RemoveZero_Always; // original pax code
324 }
325
326 return UpdateArchive(_stream, outStream, _items, updateItems,
327 options, callback);
179 328
180 COM_TRY_END 329 COM_TRY_END
181} 330}
diff --git a/CPP/7zip/Archive/Tar/TarHeader.cpp b/CPP/7zip/Archive/Tar/TarHeader.cpp
index 9c16c89..f1efddb 100644
--- a/CPP/7zip/Archive/Tar/TarHeader.cpp
+++ b/CPP/7zip/Archive/Tar/TarHeader.cpp
@@ -18,9 +18,82 @@ namespace NFileHeader {
18 // const char * const kGNUTar = "GNUtar "; // 7 chars and a null 18 // const char * const kGNUTar = "GNUtar "; // 7 chars and a null
19 // const char * const kEmpty = "\0\0\0\0\0\0\0\0"; 19 // const char * const kEmpty = "\0\0\0\0\0\0\0\0";
20 // 7-Zip used kUsTar_00 before 21.07: 20 // 7-Zip used kUsTar_00 before 21.07:
21 // const char kUsTar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ; 21 const char k_Posix_ustar_00[8] = { 'u', 's', 't', 'a', 'r', 0, '0', '0' } ;
22 // GNU TAR uses such header: 22 // GNU TAR uses such header:
23 const char kUsTar_GNU[8] = { 'u', 's', 't', 'a', 'r', ' ', ' ', 0 } ; 23 const char k_GNU_ustar__[8] = { 'u', 's', 't', 'a', 'r', ' ', ' ', 0 } ;
24 } 24 }
25 25
26/*
27pre-POSIX.1-1988 (i.e. v7) tar header:
28-----
29Link indicator:
30'0' or 0 : Normal file
31'1' : Hard link
32'2' : Symbolic link
33Some pre-POSIX.1-1988 tar implementations indicated a directory by having
34a trailing slash (/) in the name.
35
36Numeric values : octal with leading zeroes.
37For historical reasons, a final NUL or space character should also be used.
38Thus only 11 octal digits can be stored from 12 bytes field.
39
402001 star : introduced a base-256 coding that is indicated by
41setting the high-order bit of the leftmost byte of a numeric field.
42GNU-tar and BSD-tar followed this idea.
43
44versions of tar from before the first POSIX standard from 1988
45pad the values with spaces instead of zeroes.
46
47UStar
48-----
49UStar (Unix Standard TAR) : POSIX IEEE P1003.1 : 1988.
50 257 signature: "ustar", 0, "00"
51 265 32 Owner user name
52 297 32 Owner group name
53 329 8 Device major number
54 337 8 Device minor number
55 345 155 Filename prefix
56
57POSIX.1-2001/pax
58----
59format is known as extended tar format or pax format
60vendor-tagged vendor-specific enhancements.
61tags Defined by the POSIX standard:
62 atime, mtime, path, linkpath, uname, gname, size, uid, gid, ...
63
64
65PAX EXTENSION
66-----------
67Hard links
68A further difference from the ustar header block is that data blocks
69for files of typeflag 1 (hard link) may be included,
70which means that the size field may be greater than zero.
71Archives created by pax -o linkdata shall include these data
72blocks with the hard links.
73*
74
75compatiblity
76------------
77 7-Zip 16.03 supports "PaxHeader/"
78 7-Zip 20.01 supports "PaxHeaders.X/" with optional "./"
79 7-Zip 21.02 supports "@PaxHeader" with optional "./" "./"
80
81 GNU tar --format=posix uses "PaxHeaders/" in folder of file
82
83
84GNU TAR format
85==============
86v7 - Unix V7
87oldgnu - GNU tar <=1.12 : writes zero in last character in name
88gnu - GNU tar 1.13 : doesn't write zero in last character in name
89 as 7-zip 21.07
90ustar - POSIX.1-1988
91posix (pax) - POSIX.1-2001
92
93 gnu tar:
94 if (S_ISCHR (st->stat.st_mode) || S_ISBLK (st->stat.st_mode)) {
95 major_t devmajor = major (st->stat.st_rdev);
96 minor_t devminor = minor (st->stat.st_rdev); }
97*/
98
26}}} 99}}}
diff --git a/CPP/7zip/Archive/Tar/TarHeader.h b/CPP/7zip/Archive/Tar/TarHeader.h
index b0f0ec3..1af3093 100644
--- a/CPP/7zip/Archive/Tar/TarHeader.h
+++ b/CPP/7zip/Archive/Tar/TarHeader.h
@@ -59,6 +59,9 @@ namespace NFileHeader
59 const char kGnu_LongName = 'L'; 59 const char kGnu_LongName = 'L';
60 const char kSparse = 'S'; 60 const char kSparse = 'S';
61 const char kLabel = 'V'; 61 const char kLabel = 'V';
62 const char kPax = 'x'; // Extended header with meta data for the next file in the archive (POSIX.1-2001)
63 const char kPax_2 = 'X';
64 const char kGlobal = 'g'; // Global extended header with meta data (POSIX.1-2001)
62 const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR. 65 const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR.
63 data: list of files created by the --incremental (-G) option 66 data: list of files created by the --incremental (-G) option
64 Each file name is preceded by either 67 Each file name is preceded by either
@@ -66,6 +69,7 @@ namespace NFileHeader
66 - 'N' (file is a directory, or is not stored in the archive.) 69 - '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 70 Each file name is terminated by a null + an additional null after
68 the last file name. */ 71 the last file name. */
72 // 'A'-'Z' Vendor specific extensions (POSIX.1-1988)
69 } 73 }
70 74
71 extern const char * const kLongLink; // = "././@LongLink"; 75 extern const char * const kLongLink; // = "././@LongLink";
@@ -76,8 +80,8 @@ namespace NFileHeader
76 // extern const char * const kUsTar; // = "ustar"; // 5 chars 80 // extern const char * const kUsTar; // = "ustar"; // 5 chars
77 // extern const char * const kGNUTar; // = "GNUtar "; // 7 chars and a null 81 // 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" 82 // extern const char * const kEmpty; // = "\0\0\0\0\0\0\0\0"
79 // extern const char kUsTar_00[8]; 83 extern const char k_Posix_ustar_00[8];
80 extern const char kUsTar_GNU[8]; 84 extern const char k_GNU_ustar__[8];
81 } 85 }
82} 86}
83 87
diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp
index 58399d0..4fd8c5b 100644
--- a/CPP/7zip/Archive/Tar/TarIn.cpp
+++ b/CPP/7zip/Archive/Tar/TarIn.cpp
@@ -12,6 +12,45 @@
12 12
13#include "TarIn.h" 13#include "TarIn.h"
14 14
15#define NUM_UNROLL_BYTES (8 * 4)
16
17MY_NO_INLINE static bool IsBufNonZero(const void *data, size_t size);
18MY_NO_INLINE static bool IsBufNonZero(const void *data, size_t size)
19{
20 const Byte *p = (const Byte *)data;
21
22 for (; size != 0 && ((unsigned)(ptrdiff_t)p & (NUM_UNROLL_BYTES - 1)) != 0; size--)
23 if (*p++ != 0)
24 return true;
25
26 if (size >= NUM_UNROLL_BYTES)
27 {
28 const Byte *lim = p + size;
29 size &= (NUM_UNROLL_BYTES - 1);
30 lim -= size;
31 do
32 {
33 if (*(const UInt64 *)(const void *)(p ) != 0) return true;
34 if (*(const UInt64 *)(const void *)(p + 8 * 1) != 0) return true;
35 if (*(const UInt64 *)(const void *)(p + 8 * 2) != 0) return true;
36 if (*(const UInt64 *)(const void *)(p + 8 * 3) != 0) return true;
37 // if (*(const UInt32 *)(const void *)(p ) != 0) return true;
38 // if (*(const UInt32 *)(const void *)(p + 4 * 1) != 0) return true;
39 // if (*(const UInt32 *)(const void *)(p + 4 * 2) != 0) return true;
40 // if (*(const UInt32 *)(const void *)(p + 4 * 3) != 0) return true;
41 p += NUM_UNROLL_BYTES;
42 }
43 while (p != lim);
44 }
45
46 for (; size != 0; size--)
47 if (*p++ != 0)
48 return true;
49
50 return false;
51}
52
53
15namespace NArchive { 54namespace NArchive {
16namespace NTar { 55namespace NTar {
17 56
@@ -41,10 +80,11 @@ static bool OctalToNumber(const char *srcString, unsigned size, UInt64 &res, boo
41 return (*end == ' ' || *end == 0); 80 return (*end == ' ' || *end == 0);
42} 81}
43 82
44static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, bool allowEmpty = false) 83static bool OctalToNumber32(const char *srcString, UInt32 &res, bool allowEmpty = false)
45{ 84{
85 const unsigned kSize = 8;
46 UInt64 res64; 86 UInt64 res64;
47 if (!OctalToNumber(srcString, size, res64, allowEmpty)) 87 if (!OctalToNumber(srcString, kSize, res64, allowEmpty))
48 return false; 88 return false;
49 res = (UInt32)res64; 89 res = (UInt32)res64;
50 return (res64 <= 0xFFFFFFFF); 90 return (res64 <= 0xFFFFFFFF);
@@ -52,68 +92,61 @@ static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, b
52 92
53#define RIF(x) { if (!(x)) return S_OK; } 93#define RIF(x) { if (!(x)) return S_OK; }
54 94
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) 95static void ReadString(const char *s, unsigned size, AString &result)
74{ 96{
75 result.SetFrom_CalcLen(s, size); 97 result.SetFrom_CalcLen(s, size);
76} 98}
77 99
78static bool ParseInt64(const char *p, Int64 &val) 100static bool ParseInt64(const char *p, Int64 &val, bool &isBin)
79{ 101{
80 UInt32 h = GetBe32(p); 102 const UInt32 h = GetBe32(p);
81 val = (Int64)GetBe64(p + 4); 103 val = (Int64)GetBe64(p + 4);
104 isBin = true;
82 if (h == (UInt32)1 << 31) 105 if (h == (UInt32)1 << 31)
83 return ((val >> 63) & 1) == 0; 106 return ((val >> 63) & 1) == 0;
84 if (h == (UInt32)(Int32)-1) 107 if (h == (UInt32)(Int32)-1)
85 return ((val >> 63) & 1) != 0; 108 return ((val >> 63) & 1) != 0;
86 UInt64 uv; 109 isBin = false;
87 bool res = OctalToNumber(p, 12, uv); 110 UInt64 u;
88 val = (Int64)uv; 111 const bool res = OctalToNumber(p, 12, u);
112 val = (Int64)u;
89 return res; 113 return res;
90} 114}
91 115
92static bool ParseInt64_MTime(const char *p, Int64 &val) 116static bool ParseInt64_MTime(const char *p, Int64 &val, bool &isBin)
93{ 117{
94 // rare case tar : ZEROs in Docker-Windows TARs 118 // rare case tar : ZEROs in Docker-Windows TARs
95 // rare case tar : spaces 119 // rare case tar : spaces
120 isBin = false;
96 if (GetUi32(p) != 0) 121 if (GetUi32(p) != 0)
97 for (unsigned i = 0; i < 12; i++) 122 for (unsigned i = 0; i < 12; i++)
98 if (p[i] != ' ') 123 if (p[i] != ' ')
99 return ParseInt64(p, val); 124 return ParseInt64(p, val, isBin);
100 val = 0; 125 val = 0;
101 return true; 126 return true;
102} 127}
103 128
104static bool ParseSize(const char *p, UInt64 &val) 129static bool ParseSize(const char *p, UInt64 &val, bool &isBin)
105{ 130{
106 if (GetBe32(p) == (UInt32)1 << 31) 131 if (GetBe32(p) == (UInt32)1 << 31)
107 { 132 {
108 // GNU extension 133 // GNU extension
134 isBin = true;
109 val = GetBe64(p + 4); 135 val = GetBe64(p + 4);
110 return ((val >> 63) & 1) == 0; 136 return ((val >> 63) & 1) == 0;
111 } 137 }
138 isBin = false;
112 return OctalToNumber(p, 12, val, 139 return OctalToNumber(p, 12, val,
113 true // 20.03: allow empty size for 'V' Label entry 140 true // 20.03: allow empty size for 'V' Label entry
114 ); 141 );
115} 142}
116 143
144static bool ParseSize(const char *p, UInt64 &val)
145{
146 bool isBin;
147 return ParseSize(p, val, isBin);
148}
149
117#define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; } 150#define CHECK(x) { if (!(x)) return k_IsArc_Res_NO; }
118 151
119API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size) 152API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
@@ -126,26 +159,27 @@ API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size)
126 159
127 UInt32 mode; 160 UInt32 mode;
128 // we allow empty Mode value for LongName prefix items 161 // we allow empty Mode value for LongName prefix items
129 CHECK(OctalToNumber32(p, 8, mode, true)); p += 8; 162 CHECK(OctalToNumber32(p, mode, true)); p += 8;
130 163
131 // if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0; 164 // if (!OctalToNumber32(p, item.UID)) item.UID = 0;
132 p += 8; 165 p += 8;
133 // if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0; 166 // if (!OctalToNumber32(p, item.GID)) item.GID = 0;
134 p += 8; 167 p += 8;
135 168
136 UInt64 packSize; 169 UInt64 packSize;
137 Int64 time; 170 Int64 time;
138 UInt32 checkSum; 171 UInt32 checkSum;
139 CHECK(ParseSize(p, packSize)); p += 12; 172 bool isBin;
140 CHECK(ParseInt64_MTime(p, time)); p += 12; 173 CHECK(ParseSize(p, packSize, isBin)); p += 12;
141 CHECK(OctalToNumber32(p, 8, checkSum)); 174 CHECK(ParseInt64_MTime(p, time, isBin)); p += 12;
175 CHECK(OctalToNumber32(p, checkSum));
142 return k_IsArc_Res_YES; 176 return k_IsArc_Res_YES;
143} 177}
144 178
145static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) 179
180HRESULT CArchive::GetNextItemReal(CItemEx &item)
146{ 181{
147 char buf[NFileHeader::kRecordSize]; 182 char buf[NFileHeader::kRecordSize];
148 char *p = buf;
149 183
150 error = k_ErrorType_OK; 184 error = k_ErrorType_OK;
151 filled = false; 185 filled = false;
@@ -154,7 +188,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
154 for (;;) 188 for (;;)
155 { 189 {
156 size_t processedSize = NFileHeader::kRecordSize; 190 size_t processedSize = NFileHeader::kRecordSize;
157 RINOK(ReadStream(stream, buf, &processedSize)); 191 RINOK(ReadStream(SeqStream, buf, &processedSize));
158 if (processedSize == 0) 192 if (processedSize == 0)
159 { 193 {
160 if (!thereAreEmptyRecords) 194 if (!thereAreEmptyRecords)
@@ -180,10 +214,14 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
180 214
181 return S_OK; 215 return S_OK;
182 } 216 }
183 if (!IsRecordLast(buf)) 217 if (IsBufNonZero(buf, NFileHeader::kRecordSize))
184 break; 218 break;
185 item.HeaderSize += NFileHeader::kRecordSize; 219 item.HeaderSize += NFileHeader::kRecordSize;
186 thereAreEmptyRecords = true; 220 thereAreEmptyRecords = true;
221 if (OpenCallback)
222 {
223 RINOK(Progress(item, 0));
224 }
187 } 225 }
188 if (thereAreEmptyRecords) 226 if (thereAreEmptyRecords)
189 { 227 {
@@ -191,52 +229,69 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
191 return S_OK; 229 return S_OK;
192 } 230 }
193 231
232 char *p = buf;
233
194 error = k_ErrorType_Corrupted; 234 error = k_ErrorType_Corrupted;
195 ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize; 235
196 item.NameCouldBeReduced = 236 // ReadString(p, NFileHeader::kNameSize, item.Name);
237 p += NFileHeader::kNameSize;
238
239 /*
240 item.Name_CouldBeReduced =
197 (item.Name.Len() == NFileHeader::kNameSize || 241 (item.Name.Len() == NFileHeader::kNameSize ||
198 item.Name.Len() == NFileHeader::kNameSize - 1); 242 item.Name.Len() == NFileHeader::kNameSize - 1);
243 */
199 244
200 // we allow empty Mode value for LongName prefix items 245 // we allow empty Mode value for LongName prefix items
201 RIF(OctalToNumber32(p, 8, item.Mode, true)); p += 8; 246 RIF(OctalToNumber32(p, item.Mode, true)); p += 8;
202 247
203 if (!OctalToNumber32(p, 8, item.UID)) { item.UID = 0; } p += 8; 248 if (!OctalToNumber32(p, item.UID)) { item.UID = 0; } p += 8;
204 if (!OctalToNumber32(p, 8, item.GID)) { item.GID = 0; } p += 8; 249 if (!OctalToNumber32(p, item.GID)) { item.GID = 0; } p += 8;
205 250
206 RIF(ParseSize(p, item.PackSize)); 251 RIF(ParseSize(p, item.PackSize, item.PackSize_IsBin));
207 item.Size = item.PackSize; 252 item.Size = item.PackSize;
253 item.Size_IsBin = item.PackSize_IsBin;
208 p += 12; 254 p += 12;
209 RIF(ParseInt64_MTime(p, item.MTime)); p += 12; 255 RIF(ParseInt64_MTime(p, item.MTime, item.MTime_IsBin)); p += 12;
210 256
211 UInt32 checkSum; 257 UInt32 checkSum;
212 RIF(OctalToNumber32(p, 8, checkSum)); 258 RIF(OctalToNumber32(p, checkSum));
213 memset(p, ' ', 8); p += 8; 259 memset(p, ' ', 8); p += 8;
214 260
215 item.LinkFlag = *p++; 261 item.LinkFlag = *p++;
216 262
217 ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize; 263 ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize;
218 item.LinkNameCouldBeReduced = 264
265 /*
266 item.LinkName_CouldBeReduced =
219 (item.LinkName.Len() == NFileHeader::kNameSize || 267 (item.LinkName.Len() == NFileHeader::kNameSize ||
220 item.LinkName.Len() == NFileHeader::kNameSize - 1); 268 item.LinkName.Len() == NFileHeader::kNameSize - 1);
269 */
221 270
222 memcpy(item.Magic, p, 8); p += 8; 271 memcpy(item.Magic, p, 8); p += 8;
223 272
224 ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize; 273 ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize;
225 ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize; 274 ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize;
226 275
227 item.DeviceMajorDefined = (p[0] != 0); if (item.DeviceMajorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMajor)); } p += 8; 276 item.DeviceMajor_Defined = (p[0] != 0); if (item.DeviceMajor_Defined) { RIF(OctalToNumber32(p, item.DeviceMajor)); } p += 8;
228 item.DeviceMinorDefined = (p[0] != 0); if (item.DeviceMinorDefined) { RIF(OctalToNumber32(p, 8, item.DeviceMinor)); } p += 8; 277 item.DeviceMinor_Defined = (p[0] != 0); if (item.DeviceMinor_Defined) { RIF(OctalToNumber32(p, item.DeviceMinor)); } p += 8;
229 278
230 if (p[0] != 0) 279 if (p[0] != 0
280 && item.IsMagic_ustar_5chars()
281 && (item.LinkFlag != 'L' ))
231 { 282 {
232 AString prefix; 283 item.Prefix_WasUsed = true;
233 ReadString(p, NFileHeader::kPrefixSize, prefix); 284 ReadString(p, NFileHeader::kPrefixSize, item.Name);
234 if (!prefix.IsEmpty() 285 item.Name += '/';
235 && item.IsUstarMagic() 286 unsigned i;
236 && (item.LinkFlag != 'L' /* || prefix != "00000000000" */ )) 287 for (i = 0; i < NFileHeader::kNameSize; i++)
237 item.Name = prefix + '/' + item.Name; 288 if (buf[i] == 0)
289 break;
290 item.Name.AddFrom(buf, i);
238 } 291 }
239 292 else
293 ReadString(buf, NFileHeader::kNameSize, item.Name);
294
240 p += NFileHeader::kPrefixSize; 295 p += NFileHeader::kPrefixSize;
241 296
242 if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink) 297 if (item.LinkFlag == NFileHeader::NLinkFlag::kHardLink)
@@ -255,22 +310,25 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
255 310
256 /* 311 /*
257 TAR standard requires sum of unsigned byte values. 312 TAR standard requires sum of unsigned byte values.
258 But some TAR programs use sum of signed byte values. 313 But some old TAR programs use sum of signed byte values.
259 So we check both values. 314 So we check both values.
260 */ 315 */
261 UInt32 checkSumReal = 0; 316 // for (int y = 0; y < 100; y++) // for debug
262 Int32 checkSumReal_Signed = 0;
263 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
264 { 317 {
265 char c = buf[i]; 318 UInt32 sum0 = 0;
266 checkSumReal_Signed += (signed char)c; 319 {
267 checkSumReal += (Byte)buf[i]; 320 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
268 } 321 sum0 += (Byte)buf[i];
269 322 }
270 if (checkSumReal != checkSum) 323 if (sum0 != checkSum)
271 { 324 {
272 if ((UInt32)checkSumReal_Signed != checkSum) 325 Int32 sum = 0;
273 return S_OK; 326 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
327 sum += (signed char)buf[i];
328 if ((UInt32)sum != checkSum)
329 return S_OK;
330 item.IsSignedChecksum = true;
331 }
274 } 332 }
275 333
276 item.HeaderSize += NFileHeader::kRecordSize; 334 item.HeaderSize += NFileHeader::kRecordSize;
@@ -280,7 +338,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
280 Byte isExtended = (Byte)buf[482]; 338 Byte isExtended = (Byte)buf[482];
281 if (isExtended != 0 && isExtended != 1) 339 if (isExtended != 0 && isExtended != 1)
282 return S_OK; 340 return S_OK;
283 RIF(ParseSize(buf + 483, item.Size)); 341 RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin));
284 UInt64 min = 0; 342 UInt64 min = 0;
285 for (unsigned i = 0; i < 4; i++) 343 for (unsigned i = 0; i < 4; i++)
286 { 344 {
@@ -309,7 +367,7 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
309 while (isExtended != 0) 367 while (isExtended != 0)
310 { 368 {
311 size_t processedSize = NFileHeader::kRecordSize; 369 size_t processedSize = NFileHeader::kRecordSize;
312 RINOK(ReadStream(stream, buf, &processedSize)); 370 RINOK(ReadStream(SeqStream, buf, &processedSize));
313 if (processedSize != NFileHeader::kRecordSize) 371 if (processedSize != NFileHeader::kRecordSize)
314 { 372 {
315 error = k_ErrorType_UnexpectedEnd; 373 error = k_ErrorType_UnexpectedEnd;
@@ -317,6 +375,12 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
317 } 375 }
318 376
319 item.HeaderSize += NFileHeader::kRecordSize; 377 item.HeaderSize += NFileHeader::kRecordSize;
378
379 if (OpenCallback)
380 {
381 RINOK(Progress(item, 0));
382 }
383
320 isExtended = (Byte)buf[21 * 24]; 384 isExtended = (Byte)buf[21 * 24];
321 if (isExtended != 0 && isExtended != 1) 385 if (isExtended != 0 && isExtended != 1)
322 return S_OK; 386 return S_OK;
@@ -346,172 +410,711 @@ static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemE
346 return S_OK; 410 return S_OK;
347 } 411 }
348 412
413 if (item.PackSize >= (UInt64)1 << 63)
414 return S_OK;
415
349 filled = true; 416 filled = true;
350 error = k_ErrorType_OK; 417 error = k_ErrorType_OK;
351 return S_OK; 418 return S_OK;
352} 419}
353 420
354 421
355static HRESULT ReadDataToString(ISequentialInStream *stream, CItemEx &item, AString &s, EErrorType &error) 422HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset)
356{ 423{
357 const unsigned packSize = (unsigned)item.GetPackSizeAligned(); 424 const UInt64 pos = item.Get_DataPos() + posOffset;
358 size_t processedSize = packSize; 425 if (NumFiles - NumFiles_Prev < (1 << 16)
359 HRESULT res = ReadStream(stream, s.GetBuf(packSize), &processedSize); 426 // && NumRecords - NumRecords_Prev < (1 << 16)
360 item.HeaderSize += (unsigned)processedSize; 427 && pos - Pos_Prev < ((UInt32)1 << 28))
361 s.ReleaseBuf_CalcLen((unsigned)item.PackSize); 428 return S_OK;
362 RINOK(res); 429 {
363 if (processedSize != packSize) 430 Pos_Prev = pos;
364 error = k_ErrorType_UnexpectedEnd; 431 NumFiles_Prev = NumFiles;
432 // NumRecords_Prev = NumRecords;
433 // Sleep(100); // for debug
434 return OpenCallback->SetCompleted(&NumFiles, &pos);
435 }
436}
437
438
439HRESULT CArchive::ReadDataToBuffer(const CItemEx &item,
440 CTempBuffer &tb, size_t stringLimit)
441{
442 tb.Init();
443 UInt64 packSize = item.Get_PackSize_Aligned();
444 if (packSize == 0)
445 return S_OK;
446
447 UInt64 pos;
448
449 {
450 size_t size = stringLimit;
451 if (size > packSize)
452 size = (size_t)packSize;
453 tb.Buffer.AllocAtLeast(size);
454 size_t processedSize = size;
455 const HRESULT res = ReadStream(SeqStream, tb.Buffer, &processedSize);
456 pos = processedSize;
457 if (processedSize != size)
458 {
459 error = k_ErrorType_UnexpectedEnd;
460 return res;
461 }
462 RINOK(res);
463
464 packSize -= size;
465
466 size_t i;
467 const Byte *p = tb.Buffer;
468 for (i = 0; i < size; i++)
469 if (p[i] == 0)
470 break;
471 if (i >= item.PackSize)
472 tb.StringSize_IsConfirmed = true;
473 if (i > item.PackSize)
474 {
475 tb.StringSize = (size_t)item.PackSize;
476 tb.IsNonZeroTail = true;
477 }
478 else
479 {
480 tb.StringSize = i;
481 if (i != size)
482 {
483 tb.StringSize_IsConfirmed = true;
484 if (IsBufNonZero(p + i, size - i))
485 tb.IsNonZeroTail = true;
486 }
487 }
488
489 if (packSize == 0)
490 return S_OK;
491 }
492
493 if (InStream)
494 {
495 RINOK(InStream->Seek((Int64)packSize, STREAM_SEEK_CUR, NULL));
496 return S_OK;
497 }
498 const unsigned kBufSize = 1 << 15;
499 Buffer.AllocAtLeast(kBufSize);
500
501 do
502 {
503 if (OpenCallback)
504 {
505 RINOK(Progress(item, pos));
506 }
507
508 unsigned size = kBufSize;
509 if (size > packSize)
510 size = (unsigned)packSize;
511 size_t processedSize = size;
512 const HRESULT res = ReadStream(SeqStream, Buffer, &processedSize);
513 if (processedSize != size)
514 {
515 error = k_ErrorType_UnexpectedEnd;
516 return res;
517 }
518 if (!tb.IsNonZeroTail)
519 {
520 if (IsBufNonZero(Buffer, size))
521 tb.IsNonZeroTail = true;
522 }
523 packSize -= size;
524 pos += size;
525 }
526 while (packSize != 0);
365 return S_OK; 527 return S_OK;
366} 528}
529
367 530
368static bool ParsePaxLongName(const AString &src, AString &dest) 531
532struct CPaxInfo: public CPaxTimes
369{ 533{
370 dest.Empty(); 534 bool DoubleTagError;
371 for (unsigned pos = 0;;) 535 bool TagParsingError;
536 bool UnknownLines_Overflow;
537 bool Size_Defined;
538 bool UID_Defined;
539 bool GID_Defined;
540 bool Path_Defined;
541 bool Link_Defined;
542 bool User_Defined;
543 bool Group_Defined;
544
545 UInt64 Size;
546 UInt32 UID;
547 UInt32 GID;
548
549 AString Path;
550 AString Link;
551 AString User;
552 AString Group;
553 AString UnknownLines;
554
555 bool ParseID(const AString &val, bool &defined, UInt32 &res)
372 { 556 {
373 if (pos >= src.Len()) 557 if (defined)
558 DoubleTagError = true;
559 if (val.IsEmpty())
374 return false; 560 return false;
375 const char *start = src.Ptr(pos); 561 const char *end2;
376 const char *end; 562 res = ConvertStringToUInt32(val.Ptr(), &end2);
377 const UInt32 lineLen = ConvertStringToUInt32(start, &end); 563 if (*end2 != 0)
378 if (end == start)
379 return false; 564 return false;
380 if (*end != ' ') 565 defined = true;
566 return true;
567 }
568
569 bool ParsePax(const CTempBuffer &tb, bool isFile);
570};
571
572
573static bool ParsePaxTime(const AString &src, CPaxTime &pt, bool &doubleTagError)
574{
575 if (pt.IsDefined())
576 doubleTagError = true;
577 pt.Clear();
578 const char *s = src.Ptr();
579 bool isNegative = false;
580 if (*s == '-')
581 {
582 isNegative = true;
583 s++;
584 }
585 const char *end;
586 {
587 UInt64 sec = ConvertStringToUInt64(s, &end);
588 if (s == end)
381 return false; 589 return false;
382 if (lineLen > src.Len() - pos) 590 if (sec >= ((UInt64)1 << 63))
383 return false; 591 return false;
384 unsigned offset = (unsigned)(end - start) + 1; 592 if (isNegative)
385 if (lineLen < offset) 593 sec = -(Int64)sec;
594 pt.Sec = sec;
595 }
596 if (*end == 0)
597 {
598 pt.Ns = 0;
599 pt.NumDigits = 0;
600 return true;
601 }
602 if (*end != '.')
603 return false;
604 s = end + 1;
605
606 UInt32 ns = 0;
607 unsigned i;
608 const unsigned kNsDigits = 9;
609 for (i = 0;; i++)
610 {
611 const char c = s[i];
612 if (c == 0)
613 break;
614 if (c < '0' || c > '9')
386 return false; 615 return false;
387 if (IsString1PrefixedByString2(src.Ptr(pos + offset), "path=")) 616 // we ignore digits after 9 digits as GNU TAR
617 if (i < kNsDigits)
618 {
619 ns *= 10;
620 ns += c - '0';
621 }
622 }
623 pt.NumDigits = (i < kNsDigits ? i : kNsDigits);
624 while (i < kNsDigits)
625 {
626 ns *= 10;
627 i++;
628 }
629 if (isNegative && ns != 0)
630 {
631 pt.Sec--;
632 ns = (UInt32)1000 * 1000 * 1000 - ns;
633 }
634 pt.Ns = ns;
635 return true;
636}
637
638
639bool CPaxInfo::ParsePax(const CTempBuffer &tb, bool isFile)
640{
641 DoubleTagError = false;
642 TagParsingError = false;
643 UnknownLines_Overflow = false;
644 Size_Defined = false;
645 UID_Defined = false;
646 GID_Defined = false;
647 Path_Defined = false;
648 Link_Defined = false;
649 User_Defined = false;
650 Group_Defined = false;
651
652 // CPaxTimes::Clear();
653
654 const char *s = (const char *)(const void *)(const Byte *)tb.Buffer;
655 size_t rem = tb.StringSize;
656
657 Clear();
658
659 AString name, val;
660
661 while (rem != 0)
662 {
663 unsigned i;
664 for (i = 0;; i++)
388 { 665 {
389 offset += 5; // "path=" 666 if (i > 24 || i >= rem) // we use limitation for size of (size) field
390 dest = src.Mid(pos + offset, lineLen - offset);
391 if (dest.IsEmpty())
392 return false; 667 return false;
393 if (dest.Back() != '\n') 668 if (s[i] == ' ')
669 break;
670 }
671 if (i == 0)
672 return false;
673 const char *end;
674 const UInt32 size = ConvertStringToUInt32(s, &end);
675 const unsigned offset = (unsigned)(end - s) + 1;
676 if (size > rem
677 || size <= offset + 1
678 || offset != i + 1
679 || s[size - 1] != '\n')
680 return false;
681
682 for (i = offset; i < size; i++)
683 if (s[i] == 0)
394 return false; 684 return false;
395 dest.DeleteBack(); 685
396 return true; 686 for (i = offset; i < size - 1; i++)
687 if (s[i] == '=')
688 break;
689 if (i == size - 1)
690 return false;
691
692 name.SetFrom(s + offset, i - offset);
693 val.SetFrom(s + i + 1, size - 1 - (i + 1));
694
695 bool parsed = false;
696 if (isFile)
697 {
698 bool isDetectedName = true;
699 // only lower case (name) is supported
700 if (name.IsEqualTo("path"))
701 {
702 if (Path_Defined)
703 DoubleTagError = true;
704 Path = val;
705 Path_Defined = true;
706 parsed = true;
707 }
708 else if (name.IsEqualTo("linkpath"))
709 {
710 if (Link_Defined)
711 DoubleTagError = true;
712 Link = val;
713 Link_Defined = true;
714 parsed = true;
715 }
716 else if (name.IsEqualTo("uname"))
717 {
718 if (User_Defined)
719 DoubleTagError = true;
720 User = val;
721 User_Defined = true;
722 parsed = true;
723 }
724 else if (name.IsEqualTo("gname"))
725 {
726 if (Group_Defined)
727 DoubleTagError = true;
728 Group = val;
729 Group_Defined = true;
730 parsed = true;
731 }
732 else if (name.IsEqualTo("uid"))
733 {
734 parsed = ParseID(val, UID_Defined, UID);
735 }
736 else if (name.IsEqualTo("gid"))
737 {
738 parsed = ParseID(val, GID_Defined, GID);
739 }
740 else if (name.IsEqualTo("size"))
741 {
742 if (Size_Defined)
743 DoubleTagError = true;
744 Size_Defined = false;
745 if (!val.IsEmpty())
746 {
747 const char *end2;
748 Size = ConvertStringToUInt64(val.Ptr(), &end2);
749 if (*end2 == 0)
750 {
751 Size_Defined = true;
752 parsed = true;
753 }
754 }
755 }
756 else if (name.IsEqualTo("mtime"))
757 { parsed = ParsePaxTime(val, MTime, DoubleTagError); }
758 else if (name.IsEqualTo("atime"))
759 { parsed = ParsePaxTime(val, ATime, DoubleTagError); }
760 else if (name.IsEqualTo("ctime"))
761 { parsed = ParsePaxTime(val, CTime, DoubleTagError); }
762 else
763 isDetectedName = false;
764 if (isDetectedName && !parsed)
765 TagParsingError = true;
397 } 766 }
398 pos += lineLen; 767 if (!parsed)
768 {
769 if (!UnknownLines_Overflow)
770 {
771 const unsigned addSize = size - offset;
772 if (UnknownLines.Len() + addSize < (1 << 16))
773 UnknownLines.AddFrom(s + offset, addSize);
774 else
775 UnknownLines_Overflow = true;
776 }
777 }
778
779 s += size;
780 rem -= size;
399 } 781 }
782 return true;
400} 783}
401 784
402HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) 785
786HRESULT CArchive::ReadItem2(CItemEx &item)
403{ 787{
788 // CItem
789
790 item.SparseBlocks.Clear();
791 item.PaxTimes.Clear();
792
793 // CItemEx
794
404 item.HeaderSize = 0; 795 item.HeaderSize = 0;
796 item.Num_Pax_Records = 0;
405 797
406 bool flagL = false; 798 item.LongName_WasUsed = false;
407 bool flagK = false; 799 item.LongName_WasUsed_2 = false;
408 AString nameL; 800
409 AString nameK; 801 item.LongLink_WasUsed = false;
410 AString pax; 802 item.LongLink_WasUsed_2 = false;
803
804 item.HeaderError = false;
805 item.IsSignedChecksum = false;
806 item.Prefix_WasUsed = false;
411 807
412 for (;;) 808 item.Pax_Error = false;
809 item.Pax_Overflow = false;
810 item.pax_path_WasUsed = false;
811 item.pax_link_WasUsed = false;
812 item.pax_size_WasUsed = false;
813
814 item.PaxExtra.Clear();
815
816 item.EncodingCharacts.Clear();
817
818 // CArchive temp variable
819
820 NameBuf.Init();
821 LinkBuf.Init();
822 PaxBuf.Init();
823 PaxBuf_global.Init();
824
825 for (unsigned recordIndex = 0;; recordIndex++)
413 { 826 {
414 RINOK(GetNextItemReal(stream, filled, item, error)); 827 if (OpenCallback)
828 {
829 RINOK(Progress(item, 0));
830 }
831
832 RINOK(GetNextItemReal(item));
833
834 // NumRecords++;
835
415 if (!filled) 836 if (!filled)
416 { 837 {
417 if (error == k_ErrorType_OK && (flagL || flagK)) 838 if (error == k_ErrorType_OK)
839 if (item.LongName_WasUsed ||
840 item.LongLink_WasUsed ||
841 item.Num_Pax_Records != 0)
418 error = k_ErrorType_Corrupted; 842 error = k_ErrorType_Corrupted;
419 return S_OK;
420 } 843 }
421 844
422 if (error != k_ErrorType_OK) 845 if (error != k_ErrorType_OK)
423 return S_OK; 846 return S_OK;
424 847
425 if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName || // file contains a long name 848 const char lf = item.LinkFlag;
426 item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongLink) // file contains a long linkname 849 if (lf == NFileHeader::NLinkFlag::kGnu_LongName ||
850 lf == NFileHeader::NLinkFlag::kGnu_LongLink)
427 { 851 {
428 AString *name; 852 // GNU tar ignores item.Name after LinkFlag test
429 if (item.LinkFlag == NFileHeader::NLinkFlag::kGnu_LongName) 853 // 22.00 : now we also ignore item.Name here
430 { if (flagL) return S_OK; flagL = true; name = &nameL; } 854 /*
431 else
432 { if (flagK) return S_OK; flagK = true; name = &nameK; }
433
434 if (item.Name != NFileHeader::kLongLink && 855 if (item.Name != NFileHeader::kLongLink &&
435 item.Name != NFileHeader::kLongLink2) 856 item.Name != NFileHeader::kLongLink2)
857 {
858 break;
859 // return S_OK;
860 }
861 */
862
863 CTempBuffer *tb =
864 lf == NFileHeader::NLinkFlag::kGnu_LongName ?
865 &NameBuf :
866 &LinkBuf;
867
868 /*
869 if (item.PackSize > (1 << 29))
870 {
871 // break;
436 return S_OK; 872 return S_OK;
437 if (item.PackSize > (1 << 14)) 873 }
438 return S_OK; 874 */
439 875
440 RINOK(ReadDataToString(stream, item, *name, error)); 876 const unsigned kLongNameSizeMax = (unsigned)1 << 14;
877 RINOK(ReadDataToBuffer(item, *tb, kLongNameSizeMax));
441 if (error != k_ErrorType_OK) 878 if (error != k_ErrorType_OK)
442 return S_OK; 879 return S_OK;
443 880
881 if (lf == NFileHeader::NLinkFlag::kGnu_LongName)
882 {
883 item.LongName_WasUsed_2 =
884 item.LongName_WasUsed;
885 item.LongName_WasUsed = true;
886 }
887 else
888 {
889 item.LongLink_WasUsed_2 =
890 item.LongLink_WasUsed;
891 item.LongLink_WasUsed = true;
892 }
893
894 if (!tb->StringSize_IsConfirmed)
895 tb->StringSize = 0;
896 item.HeaderSize += item.Get_PackSize_Aligned();
897 if (tb->StringSize == 0 ||
898 tb->StringSize + 1 != item.PackSize)
899 item.HeaderError = true;
900 if (tb->IsNonZeroTail)
901 item.HeaderError = true;
444 continue; 902 continue;
445 } 903 }
446 904
447 switch (item.LinkFlag) 905 if (lf == NFileHeader::NLinkFlag::kGlobal ||
906 lf == NFileHeader::NLinkFlag::kPax ||
907 lf == NFileHeader::NLinkFlag::kPax_2)
448 { 908 {
449 case 'g': 909 // GNU tar ignores item.Name after LinkFlag test
450 case 'x': 910 // 22.00 : now we also ignore item.Name here
451 case 'X': 911 /*
912 if (item.PackSize > (UInt32)1 << 26)
452 { 913 {
453 const char *s = item.Name.Ptr(); 914 break; // we don't want big PaxBuf files
454 if (IsString1PrefixedByString2(s, "./")) 915 // return S_OK;
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 } 916 }
471 case NFileHeader::NLinkFlag::kDumpDir: 917 */
918 const unsigned kParsingPaxSizeMax = (unsigned)1 << 26;
919
920 const bool isStartHeader = (item.HeaderSize == NFileHeader::kRecordSize);
921
922 CTempBuffer *tb = (lf == NFileHeader::NLinkFlag::kGlobal ? &PaxBuf_global : &PaxBuf);
923
924 RINOK(ReadDataToBuffer(item, *tb, kParsingPaxSizeMax));
925 if (error != k_ErrorType_OK)
926 return S_OK;
927
928 item.HeaderSize += item.Get_PackSize_Aligned();
929
930 if (tb->StringSize != item.PackSize
931 || tb->StringSize == 0
932 || tb->IsNonZeroTail)
933 item.Pax_Error = true;
934
935 item.Num_Pax_Records++;
936 if (lf != NFileHeader::NLinkFlag::kGlobal)
472 { 937 {
473 break; 938 item.PaxExtra.RecordPath = item.Name;
474 // GNU Extensions to the Archive Format 939 continue;
475 } 940 }
476 case NFileHeader::NLinkFlag::kSparse: 941 // break; // for debug
477 { 942 {
478 break; 943 if (PaxGlobal_Defined)
479 // GNU Extensions to the Archive Format 944 _is_PaxGlobal_Error = true;
945 CPaxInfo paxInfo;
946 if (paxInfo.ParsePax(PaxBuf_global, false))
947 {
948 PaxGlobal.RawLines = paxInfo.UnknownLines;
949 PaxGlobal.RecordPath = item.Name;
950 PaxGlobal_Defined = true;
951 }
952 else
953 _is_PaxGlobal_Error = true;
954 if (isStartHeader)
955 {
956 // we skip global pax header info after parsing
957 item.HeaderPos += item.HeaderSize;
958 item.HeaderSize = 0;
959 }
480 } 960 }
481 default: 961 continue;
482 if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0))
483 return S_OK;
484 } 962 }
485 963
486 if (flagL) 964 /*
965 if (lf == NFileHeader::NLinkFlag::kDumpDir ||
966 lf == NFileHeader::NLinkFlag::kSparse)
487 { 967 {
488 item.Name = nameL; 968 // GNU Extensions to the Archive Format
489 item.NameCouldBeReduced = false; 969 break;
490 } 970 }
491 971 if (lf > '7' || (lf < '0' && lf != 0))
492 if (flagK)
493 { 972 {
494 item.LinkName = nameK; 973 break;
495 item.LinkNameCouldBeReduced = false; 974 // return S_OK;
496 } 975 }
497 976 */
498 error = k_ErrorType_OK; 977 break;
499 978 }
500 if (!pax.IsEmpty()) 979
980 // we still use name from main header, if long_name is bad
981 if (item.LongName_WasUsed && NameBuf.StringSize != 0)
982 {
983 NameBuf.CopyToString(item.Name);
984 // item.Name_CouldBeReduced = false;
985 }
986
987 if (item.LongLink_WasUsed)
988 {
989 // we use empty link, if long_link is bad
990 LinkBuf.CopyToString(item.LinkName);
991 // item.LinkName_CouldBeReduced = false;
992 }
993
994 error = k_ErrorType_OK;
995
996 if (PaxBuf.StringSize != 0)
997 {
998 CPaxInfo paxInfo;
999 if (!paxInfo.ParsePax(PaxBuf, true))
1000 item.Pax_Error = true;
1001 else
501 { 1002 {
502 AString name; 1003 if (paxInfo.Path_Defined) // if (!paxInfo.Path.IsEmpty())
503 if (ParsePaxLongName(pax, name)) 1004 {
504 item.Name = name; 1005 item.Name = paxInfo.Path;
505 else 1006 item.pax_path_WasUsed = true;
1007 }
1008 if (paxInfo.Link_Defined) // (!paxInfo.Link.IsEmpty())
1009 {
1010 item.LinkName = paxInfo.Link;
1011 item.pax_link_WasUsed = true;
1012 }
1013 if (paxInfo.User_Defined)
1014 {
1015 item.User = paxInfo.User;
1016 // item.pax_uname_WasUsed = true;
1017 }
1018 if (paxInfo.Group_Defined)
1019 {
1020 item.Group = paxInfo.Group;
1021 // item.pax_gname_WasUsed = true;
1022 }
1023 if (paxInfo.UID_Defined)
506 { 1024 {
507 // no "path" property is allowed in pax4467 1025 item.UID = (UInt32)paxInfo.UID;
508 // error = k_ErrorType_Warning;
509 } 1026 }
510 pax.Empty(); 1027 if (paxInfo.GID_Defined)
1028 {
1029 item.GID = (UInt32)paxInfo.GID;
1030 }
1031
1032 if (paxInfo.Size_Defined)
1033 {
1034 const UInt64 piSize = paxInfo.Size;
1035 // GNU TAR ignores (item.Size) in that case
1036 if (item.Size != 0 && item.Size != piSize)
1037 item.Pax_Error = true;
1038 item.Size = piSize;
1039 item.PackSize = piSize;
1040 item.pax_size_WasUsed = true;
1041 }
1042
1043 item.PaxTimes = paxInfo;
1044 item.PaxExtra.RawLines = paxInfo.UnknownLines;
1045 if (paxInfo.UnknownLines_Overflow)
1046 item.Pax_Overflow = true;
1047 if (paxInfo.TagParsingError)
1048 item.Pax_Error = true;
1049 if (paxInfo.DoubleTagError)
1050 item.Pax_Error = true;
511 } 1051 }
1052 }
512 1053
513 return S_OK; 1054 return S_OK;
1055}
1056
1057
1058
1059HRESULT CArchive::ReadItem(CItemEx &item)
1060{
1061 item.HeaderPos = _phySize;
1062
1063 const HRESULT res = ReadItem2(item);
1064
1065 /*
1066 if (error == k_ErrorType_Warning)
1067 _is_Warning = true;
1068 else
1069 */
1070
1071 if (error != k_ErrorType_OK)
1072 _error = error;
1073
1074 RINOK(res);
1075
1076 if (filled)
1077 {
1078 if (item.IsMagic_GNU())
1079 _are_Gnu = true;
1080 else if (item.IsMagic_Posix_ustar_00())
1081 _are_Posix = true;
1082
1083 if (item.Num_Pax_Records != 0)
1084 _are_Pax = true;
1085
1086 if (item.PaxTimes.MTime.IsDefined()) _are_mtime = true;
1087 if (item.PaxTimes.ATime.IsDefined()) _are_atime = true;
1088 if (item.PaxTimes.CTime.IsDefined()) _are_ctime = true;
1089
1090 if (item.pax_path_WasUsed)
1091 _are_pax_path = true;
1092 if (item.pax_link_WasUsed)
1093 _are_pax_link = true;
1094 if (item.LongName_WasUsed)
1095 _are_LongName = true;
1096 if (item.LongLink_WasUsed)
1097 _are_LongLink = true;
1098 if (item.Prefix_WasUsed)
1099 _pathPrefix_WasUsed = true;
1100 /*
1101 if (item.IsSparse())
1102 _isSparse = true;
1103 */
1104 if (item.Is_PaxExtendedHeader())
1105 _are_Pax_Items = true;
1106 if (item.IsThereWarning()
1107 || item.HeaderError
1108 || item.Pax_Error)
1109 _is_Warning = true;
514 } 1110 }
1111
1112 const UInt64 headerEnd = item.HeaderPos + item.HeaderSize;
1113 // _headersSize += headerEnd - _phySize;
1114 // we don't count skipped records
1115 _headersSize += item.HeaderSize;
1116 _phySize = headerEnd;
1117 return S_OK;
515} 1118}
516 1119
517}} 1120}}
diff --git a/CPP/7zip/Archive/Tar/TarIn.h b/CPP/7zip/Archive/Tar/TarIn.h
index 1c508bc..e99599a 100644
--- a/CPP/7zip/Archive/Tar/TarIn.h
+++ b/CPP/7zip/Archive/Tar/TarIn.h
@@ -3,7 +3,7 @@
3#ifndef __ARCHIVE_TAR_IN_H 3#ifndef __ARCHIVE_TAR_IN_H
4#define __ARCHIVE_TAR_IN_H 4#define __ARCHIVE_TAR_IN_H
5 5
6#include "../../IStream.h" 6#include "../IArchive.h"
7 7
8#include "TarItem.h" 8#include "TarItem.h"
9 9
@@ -14,11 +14,133 @@ enum EErrorType
14{ 14{
15 k_ErrorType_OK, 15 k_ErrorType_OK,
16 k_ErrorType_Corrupted, 16 k_ErrorType_Corrupted,
17 k_ErrorType_UnexpectedEnd, 17 k_ErrorType_UnexpectedEnd
18 k_ErrorType_Warning 18 // , k_ErrorType_Warning
19};
20
21
22struct CTempBuffer
23{
24 CByteBuffer Buffer;
25 size_t StringSize; // num characters before zero Byte (StringSize <= item.PackSize)
26 bool IsNonZeroTail;
27 bool StringSize_IsConfirmed;
28
29 void CopyToString(AString &s)
30 {
31 s.Empty();
32 if (StringSize != 0)
33 s.SetFrom((const char *)(const void *)(const Byte *)Buffer, (unsigned)StringSize);
34 }
35
36 void Init()
37 {
38 StringSize = 0;
39 IsNonZeroTail = false;
40 StringSize_IsConfirmed = false;
41 }
42};
43
44
45class CArchive
46{
47public:
48 bool _phySize_Defined;
49 bool _is_Warning;
50 bool PaxGlobal_Defined;
51 bool _is_PaxGlobal_Error;
52 bool _are_Pax_Items;
53 bool _are_Gnu;
54 bool _are_Posix;
55 bool _are_Pax;
56 bool _are_mtime;
57 bool _are_atime;
58 bool _are_ctime;
59 bool _are_pax_path;
60 bool _are_pax_link;
61 bool _are_LongName;
62 bool _are_LongLink;
63 bool _pathPrefix_WasUsed;
64 // bool _isSparse;
65
66 // temp internal vars for ReadItem():
67 bool filled;
68private:
69 EErrorType error;
70
71public:
72 UInt64 _phySize;
73 UInt64 _headersSize;
74 EErrorType _error;
75
76 ISequentialInStream *SeqStream;
77 IInStream *InStream;
78 IArchiveOpenCallback *OpenCallback;
79 UInt64 NumFiles;
80 UInt64 NumFiles_Prev;
81 UInt64 Pos_Prev;
82 // UInt64 NumRecords;
83 // UInt64 NumRecords_Prev;
84
85 CPaxExtra PaxGlobal;
86
87 void Clear()
88 {
89 SeqStream = NULL;
90 InStream = NULL;
91 OpenCallback = NULL;
92 NumFiles = 0;
93 NumFiles_Prev = 0;
94 Pos_Prev = 0;
95 // NumRecords = 0;
96 // NumRecords_Prev = 0;
97
98 PaxGlobal.Clear();
99 PaxGlobal_Defined = false;
100 _is_PaxGlobal_Error = false;
101 _are_Pax_Items = false; // if there are final paxItems
102 _are_Gnu = false;
103 _are_Posix = false;
104 _are_Pax = false;
105 _are_mtime = false;
106 _are_atime = false;
107 _are_ctime = false;
108 _are_pax_path = false;
109 _are_pax_link = false;
110 _are_LongName = false;
111 _are_LongLink = false;
112 _pathPrefix_WasUsed = false;
113 // _isSparse = false;
114
115 _is_Warning = false;
116 _error = k_ErrorType_OK;
117
118 _phySize_Defined = false;
119 _phySize = 0;
120 _headersSize = 0;
121 }
122
123private:
124 CTempBuffer NameBuf;
125 CTempBuffer LinkBuf;
126 CTempBuffer PaxBuf;
127 CTempBuffer PaxBuf_global;
128
129 CByteBuffer Buffer;
130
131 HRESULT ReadDataToBuffer(const CItemEx &item, CTempBuffer &tb, size_t stringLimit);
132 HRESULT Progress(const CItemEx &item, UInt64 posOffset);
133 HRESULT GetNextItemReal(CItemEx &item);
134 HRESULT ReadItem2(CItemEx &itemInfo);
135public:
136 CArchive()
137 {
138 // we will call Clear() in CHandler::Close().
139 // Clear(); // it's not required here
140 }
141 HRESULT ReadItem(CItemEx &itemInfo);
19}; 142};
20 143
21HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, EErrorType &error);
22 144
23API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size); 145API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size);
24 146
diff --git a/CPP/7zip/Archive/Tar/TarItem.h b/CPP/7zip/Archive/Tar/TarItem.h
index f947786..738618f 100644
--- a/CPP/7zip/Archive/Tar/TarItem.h
+++ b/CPP/7zip/Archive/Tar/TarItem.h
@@ -6,8 +6,6 @@
6#include "../../../Common/MyLinux.h" 6#include "../../../Common/MyLinux.h"
7#include "../../../Common/UTFConvert.h" 7#include "../../../Common/UTFConvert.h"
8 8
9#include "../Common/ItemNameUtils.h"
10
11#include "TarHeader.h" 9#include "TarHeader.h"
12 10
13namespace NArchive { 11namespace NArchive {
@@ -19,50 +17,149 @@ struct CSparseBlock
19 UInt64 Size; 17 UInt64 Size;
20}; 18};
21 19
20
21enum EPaxTimeRemoveZeroMode
22{
23 k_PaxTimeMode_DontRemoveZero,
24 k_PaxTimeMode_RemoveZero_if_PureSecondOnly,
25 k_PaxTimeMode_RemoveZero_Always
26};
27
28struct CTimeOptions
29{
30 EPaxTimeRemoveZeroMode RemoveZeroMode;
31 unsigned NumDigitsMax;
32
33 void Init()
34 {
35 RemoveZeroMode = k_PaxTimeMode_RemoveZero_if_PureSecondOnly;
36 NumDigitsMax = 0;
37 }
38 CTimeOptions() { Init(); }
39};
40
41
42struct CPaxTime
43{
44 Int32 NumDigits; // -1 means undefined
45 UInt32 Ns; // it's smaller than 1G. Even if (Sec < 0), larger (Ns) value means newer files.
46 Int64 Sec; // can be negative
47
48 Int64 GetSec() const { return NumDigits != -1 ? Sec : 0; }
49
50 bool IsDefined() const { return NumDigits != -1; }
51 // bool IsDefined_And_nonZero() const { return NumDigits != -1 && (Sec != 0 || Ns != 0); }
52
53 void Clear()
54 {
55 NumDigits = -1;
56 Ns = 0;
57 Sec = 0;
58 }
59 CPaxTime() { Clear(); }
60
61 /*
62 void ReducePrecison(int numDigits)
63 {
64 // we don't use this->NumDigits here
65 if (numDigits > 0)
66 {
67 if (numDigits >= 9)
68 return;
69 UInt32 r = 1;
70 for (unsigned i = numDigits; i < 9; i++)
71 r *= 10;
72 Ns /= r;
73 Ns *= r;
74 return;
75 }
76 Ns = 0;
77 if (numDigits == 0)
78 return;
79 UInt32 r;
80 if (numDigits == -1) r = 60;
81 else if (numDigits == -2) r = 60 * 60;
82 else if (numDigits == -3) r = 60 * 60 * 24;
83 else return;
84 Sec /= r;
85 Sec *= r;
86 }
87 */
88};
89
90
91struct CPaxTimes
92{
93 CPaxTime MTime;
94 CPaxTime ATime;
95 CPaxTime CTime;
96
97 void Clear()
98 {
99 MTime.Clear();
100 ATime.Clear();
101 CTime.Clear();
102 }
103
104 /*
105 void ReducePrecison(int numDigits)
106 {
107 MTime.ReducePrecison(numDigits);
108 CTime.ReducePrecison(numDigits);
109 ATime.ReducePrecison(numDigits);
110 }
111 */
112};
113
114
22struct CItem 115struct CItem
23{ 116{
24 AString Name;
25 UInt64 PackSize; 117 UInt64 PackSize;
26 UInt64 Size; 118 UInt64 Size;
27 Int64 MTime; 119 Int64 MTime;
28 120
121 char LinkFlag;
122 bool DeviceMajor_Defined;
123 bool DeviceMinor_Defined;
124
29 UInt32 Mode; 125 UInt32 Mode;
30 UInt32 UID; 126 UInt32 UID;
31 UInt32 GID; 127 UInt32 GID;
32 UInt32 DeviceMajor; 128 UInt32 DeviceMajor;
33 UInt32 DeviceMinor; 129 UInt32 DeviceMinor;
34 130
131 AString Name;
35 AString LinkName; 132 AString LinkName;
36 AString User; 133 AString User;
37 AString Group; 134 AString Group;
38 135
39 char Magic[8]; 136 char Magic[8];
40 char LinkFlag; 137
41 bool DeviceMajorDefined; 138 CPaxTimes PaxTimes;
42 bool DeviceMinorDefined;
43 139
44 CRecordVector<CSparseBlock> SparseBlocks; 140 CRecordVector<CSparseBlock> SparseBlocks;
45 141
46 void SetDefaultWriteFields() 142 void SetMagic_Posix(bool posixMode)
47 { 143 {
48 DeviceMajorDefined = false; 144 memcpy(Magic, posixMode ?
49 DeviceMinorDefined = false; 145 NFileHeader::NMagic::k_Posix_ustar_00 :
50 UID = 0; 146 NFileHeader::NMagic::k_GNU_ustar__,
51 GID = 0; 147 8);
52 memcpy(Magic, NFileHeader::NMagic::kUsTar_GNU, 8);
53 } 148 }
54 149
55 bool IsSymLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymLink && (Size == 0); } 150 bool Is_SymLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymLink && (Size == 0); }
56 bool IsHardLink() const { return LinkFlag == NFileHeader::NLinkFlag::kHardLink; } 151 bool Is_HardLink() const { return LinkFlag == NFileHeader::NLinkFlag::kHardLink; }
57 bool IsSparse() const { return LinkFlag == NFileHeader::NLinkFlag::kSparse; } 152 bool Is_Sparse() const { return LinkFlag == NFileHeader::NLinkFlag::kSparse; }
58 UInt64 GetUnpackSize() const { return IsSymLink() ? LinkName.Len() : Size; } 153
59 bool IsPaxExtendedHeader() const 154 UInt64 Get_UnpackSize() const { return Is_SymLink() ? LinkName.Len() : Size; }
155
156 bool Is_PaxExtendedHeader() const
60 { 157 {
61 switch (LinkFlag) 158 switch (LinkFlag)
62 { 159 {
63 case 'g': 160 case NFileHeader::NLinkFlag::kPax:
64 case 'x': 161 case NFileHeader::NLinkFlag::kPax_2:
65 case 'X': // Check it 162 case NFileHeader::NLinkFlag::kGlobal:
66 return true; 163 return true;
67 } 164 }
68 return false; 165 return false;
@@ -72,6 +169,17 @@ struct CItem
72 { 169 {
73 return (Mode & ~(UInt32)MY_LIN_S_IFMT) | Get_FileTypeMode_from_LinkFlag(); 170 return (Mode & ~(UInt32)MY_LIN_S_IFMT) | Get_FileTypeMode_from_LinkFlag();
74 } 171 }
172
173 void Set_LinkFlag_for_File(UInt32 mode)
174 {
175 Byte lf = NFileHeader::NLinkFlag::kNormal;
176 if (MY_LIN_S_ISCHR(mode)) lf = NFileHeader::NLinkFlag::kCharacter;
177 else if (MY_LIN_S_ISBLK(mode)) lf = NFileHeader::NLinkFlag::kBlock;
178 else if (MY_LIN_S_ISFIFO(mode)) lf = NFileHeader::NLinkFlag::kFIFO;
179 // else if (MY_LIN_S_ISDIR(mode)) lf = NFileHeader::NLinkFlag::kDirectory;
180 // else if (MY_LIN_S_ISLNK(mode)) lf = NFileHeader::NLinkFlag::kSymLink;
181 LinkFlag = lf;
182 }
75 183
76 UInt32 Get_FileTypeMode_from_LinkFlag() const 184 UInt32 Get_FileTypeMode_from_LinkFlag() const
77 { 185 {
@@ -82,10 +190,10 @@ struct CItem
82 case NFileHeader::NLinkFlag::kDumpDir: 190 case NFileHeader::NLinkFlag::kDumpDir:
83 return MY_LIN_S_IFDIR; 191 return MY_LIN_S_IFDIR;
84 */ 192 */
85 case NFileHeader::NLinkFlag::kSymLink: return MY_LIN_S_IFLNK; 193 case NFileHeader::NLinkFlag::kSymLink: return MY_LIN_S_IFLNK;
86 case NFileHeader::NLinkFlag::kBlock: return MY_LIN_S_IFBLK; 194 case NFileHeader::NLinkFlag::kBlock: return MY_LIN_S_IFBLK;
87 case NFileHeader::NLinkFlag::kCharacter: return MY_LIN_S_IFCHR; 195 case NFileHeader::NLinkFlag::kCharacter: return MY_LIN_S_IFCHR;
88 case NFileHeader::NLinkFlag::kFIFO: return MY_LIN_S_IFIFO; 196 case NFileHeader::NLinkFlag::kFIFO: return MY_LIN_S_IFIFO;
89 // case return MY_LIN_S_IFSOCK; 197 // case return MY_LIN_S_IFSOCK;
90 } 198 }
91 199
@@ -104,20 +212,41 @@ struct CItem
104 case NFileHeader::NLinkFlag::kOldNormal: 212 case NFileHeader::NLinkFlag::kOldNormal:
105 case NFileHeader::NLinkFlag::kNormal: 213 case NFileHeader::NLinkFlag::kNormal:
106 case NFileHeader::NLinkFlag::kSymLink: 214 case NFileHeader::NLinkFlag::kSymLink:
107 return NItemName::HasTailSlash(Name, CP_OEMCP); 215 if (Name.IsEmpty())
216 return false;
217 // GNU TAR uses last character as directory marker
218 // we also do it
219 return Name.Back() == '/';
220 // return NItemName::HasTailSlash(Name, CP_OEMCP);
108 } 221 }
109 return false; 222 return false;
110 } 223 }
111 224
112 bool IsUstarMagic() const 225 bool IsMagic_ustar_5chars() const
226 {
227 for (unsigned i = 0; i < 5; i++)
228 if (Magic[i] != NFileHeader::NMagic::k_GNU_ustar__[i])
229 return false;
230 return true;
231 }
232
233 bool IsMagic_Posix_ustar_00() const
234 {
235 for (unsigned i = 0; i < 8; i++)
236 if (Magic[i] != NFileHeader::NMagic::k_Posix_ustar_00[i])
237 return false;
238 return true;
239 }
240
241 bool IsMagic_GNU() const
113 { 242 {
114 for (int i = 0; i < 5; i++) 243 for (unsigned i = 0; i < 8; i++)
115 if (Magic[i] != NFileHeader::NMagic::kUsTar_GNU[i]) 244 if (Magic[i] != NFileHeader::NMagic::k_GNU_ustar__[i])
116 return false; 245 return false;
117 return true; 246 return true;
118 } 247 }
119 248
120 UInt64 GetPackSizeAligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); } 249 UInt64 Get_PackSize_Aligned() const { return (PackSize + 0x1FF) & (~((UInt64)0x1FF)); }
121 250
122 bool IsThereWarning() const 251 bool IsThereWarning() const
123 { 252 {
@@ -163,18 +292,67 @@ struct CEncodingCharacts
163}; 292};
164 293
165 294
295struct CPaxExtra
296{
297 AString RecordPath;
298 AString RawLines;
299
300 void Clear()
301 {
302 RecordPath.Empty();
303 RawLines.Empty();
304 }
305
306 void Print_To_String(AString &s) const
307 {
308 if (!RecordPath.IsEmpty())
309 {
310 s += RecordPath;
311 s.Add_LF();
312 }
313 if (!RawLines.IsEmpty())
314 s += RawLines;
315 }
316};
317
166 318
167struct CItemEx: public CItem 319struct CItemEx: public CItem
168{ 320{
321 bool HeaderError;
322
323 bool IsSignedChecksum;
324 bool Prefix_WasUsed;
325
326 bool Pax_Error;
327 bool Pax_Overflow;
328 bool pax_path_WasUsed;
329 bool pax_link_WasUsed;
330 bool pax_size_WasUsed;
331
332 bool MTime_IsBin;
333 bool PackSize_IsBin;
334 bool Size_IsBin;
335
336 bool LongName_WasUsed;
337 bool LongName_WasUsed_2;
338
339 bool LongLink_WasUsed;
340 bool LongLink_WasUsed_2;
341
342 // bool Name_CouldBeReduced;
343 // bool LinkName_CouldBeReduced;
344
169 UInt64 HeaderPos; 345 UInt64 HeaderPos;
170 unsigned HeaderSize; 346 UInt64 HeaderSize;
171 bool NameCouldBeReduced; 347
172 bool LinkNameCouldBeReduced; 348 UInt64 Num_Pax_Records;
349 CPaxExtra PaxExtra;
173 350
174 CEncodingCharacts EncodingCharacts; 351 CEncodingCharacts EncodingCharacts;
175 352
176 UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; } 353 UInt64 Get_DataPos() const { return HeaderPos + HeaderSize; }
177 UInt64 GetFullSize() const { return HeaderSize + PackSize; } 354 // UInt64 GetFullSize() const { return HeaderSize + PackSize; }
355 UInt64 Get_FullSize_Aligned() const { return HeaderSize + Get_PackSize_Aligned(); }
178}; 356};
179 357
180}} 358}}
diff --git a/CPP/7zip/Archive/Tar/TarOut.cpp b/CPP/7zip/Archive/Tar/TarOut.cpp
index 271b854..f73c625 100644
--- a/CPP/7zip/Archive/Tar/TarOut.cpp
+++ b/CPP/7zip/Archive/Tar/TarOut.cpp
@@ -2,6 +2,10 @@
2 2
3#include "StdAfx.h" 3#include "StdAfx.h"
4 4
5#include "../../../../C/7zCrc.h"
6
7#include "../../../Common/IntToString.h"
8
5#include "../../Common/StreamUtils.h" 9#include "../../Common/StreamUtils.h"
6 10
7#include "TarOut.h" 11#include "TarOut.h"
@@ -9,23 +13,27 @@
9namespace NArchive { 13namespace NArchive {
10namespace NTar { 14namespace NTar {
11 15
12HRESULT COutArchive::WriteBytes(const void *data, unsigned size) 16using namespace NFileHeader;
13{ 17
14 Pos += size; 18// it's path prefix assigned by 7-Zip to show that file path was cut
15 return WriteStream(m_Stream, data, size); 19#define K_PREFIX_PATH_CUT "@PathCut"
16} 20
21static const UInt32 k_7_oct_digits_Val_Max = ((UInt32)1 << (7 * 3)) - 1;
17 22
18static bool WriteOctal_8(char *s, UInt32 val) 23static void WriteOctal_8(char *s, UInt32 val)
19{ 24{
20 const unsigned kNumDigits = 8 - 1; 25 const unsigned kNumDigits = 8 - 1;
21 if (val >= ((UInt32)1 << (kNumDigits * 3))) 26 if (val >= ((UInt32)1 << (kNumDigits * 3)))
22 return false; 27 {
28 val = 0;
29 // return false;
30 }
23 for (unsigned i = 0; i < kNumDigits; i++) 31 for (unsigned i = 0; i < kNumDigits; i++)
24 { 32 {
25 s[kNumDigits - 1 - i] = (char)('0' + (val & 7)); 33 s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
26 val >>= 3; 34 val >>= 3;
27 } 35 }
28 return true; 36 // return true;
29} 37}
30 38
31static void WriteBin_64bit(char *s, UInt64 val) 39static void WriteBin_64bit(char *s, UInt64 val)
@@ -68,61 +76,93 @@ static void CopyString(char *dest, const AString &src, unsigned maxSize)
68 unsigned len = src.Len(); 76 unsigned len = src.Len();
69 if (len == 0) 77 if (len == 0)
70 return; 78 return;
71 // 21.07: we don't require additional 0 character at the end 79 // 21.07: new gnu : we don't require additional 0 character at the end
80 // if (len >= maxSize)
72 if (len > maxSize) 81 if (len > maxSize)
73 { 82 {
74 len = maxSize; 83 len = maxSize;
75 // return false; 84 /*
85 // oldgnu needs 0 character at the end
86 len = maxSize - 1;
87 dest[len] = 0;
88 */
76 } 89 }
77 memcpy(dest, src.Ptr(), len); 90 memcpy(dest, src.Ptr(), len);
78 // return true;
79} 91}
80 92
81#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; } 93// #define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_INVALIDARG; }
94#define RETURN_IF_NOT_TRUE(x) { x; }
82 95
83#define COPY_STRING_CHECK(dest, src, size) \ 96#define COPY_STRING_CHECK(dest, src, size) \
84 CopyString(dest, src, size); dest += (size); 97 CopyString(dest, src, size); dest += (size);
85 98
86#define WRITE_OCTAL_8_CHECK(dest, src) \ 99#define WRITE_OCTAL_8_CHECK(dest, src) \
87 RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src)); 100 RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src))
88 101
89 102
90HRESULT COutArchive::WriteHeaderReal(const CItem &item) 103HRESULT COutArchive::WriteHeaderReal(const CItem &item, bool isPax
104 // , bool zero_PackSize
105 // , bool zero_MTime
106 )
91{ 107{
92 char record[NFileHeader::kRecordSize]; 108 /*
93 memset(record, 0, NFileHeader::kRecordSize); 109 if (isPax) { we don't use Glob_Name and Prefix }
110 if (!isPax)
111 {
112 we use Glob_Name if it's not empty
113 we use Prefix if it's not empty
114 }
115 */
116 char record[kRecordSize];
117 memset(record, 0, kRecordSize);
94 char *cur = record; 118 char *cur = record;
95 119
96 COPY_STRING_CHECK (cur, item.Name, NFileHeader::kNameSize); 120 COPY_STRING_CHECK (cur,
121 (!isPax && !Glob_Name.IsEmpty()) ? Glob_Name : item.Name,
122 kNameSize);
97 123
98 WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8; 124 WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8; // & k_7_oct_digits_Val_Max
99 WRITE_OCTAL_8_CHECK (cur, item.UID); cur += 8; 125 WRITE_OCTAL_8_CHECK (cur, item.UID); cur += 8;
100 WRITE_OCTAL_8_CHECK (cur, item.GID); cur += 8; 126 WRITE_OCTAL_8_CHECK (cur, item.GID); cur += 8;
101 127
102 WriteOctal_12(cur, item.PackSize); cur += 12; 128 WriteOctal_12 (cur, /* zero_PackSize ? 0 : */ item.PackSize); cur += 12;
103 WriteOctal_12_Signed(cur, item.MTime); cur += 12; 129 WriteOctal_12_Signed (cur, /* zero_MTime ? 0 : */ item.MTime); cur += 12;
104 130
105 memset(cur, ' ', 8); // checksum field 131 // we will use binary init for checksum instead of memset
132 // checksum field:
133 // memset(cur, ' ', 8);
106 cur += 8; 134 cur += 8;
107 135
108 *cur++ = item.LinkFlag; 136 *cur++ = item.LinkFlag;
109 137
110 COPY_STRING_CHECK (cur, item.LinkName, NFileHeader::kNameSize); 138 COPY_STRING_CHECK (cur, item.LinkName, kNameSize);
111 139
112 memcpy(cur, item.Magic, 8); 140 memcpy(cur, item.Magic, 8);
113 cur += 8; 141 cur += 8;
114 142
115 COPY_STRING_CHECK (cur, item.User, NFileHeader::kUserNameSize); 143 COPY_STRING_CHECK (cur, item.User, kUserNameSize);
116 COPY_STRING_CHECK (cur, item.Group, NFileHeader::kGroupNameSize); 144 COPY_STRING_CHECK (cur, item.Group, kGroupNameSize);
117 145
118 if (item.DeviceMajorDefined) 146 const bool needDevice = (IsPosixMode && !isPax);
119 WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor); 147
148 if (item.DeviceMajor_Defined)
149 WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor)
150 else if (needDevice)
151 WRITE_OCTAL_8_CHECK (cur, 0)
120 cur += 8; 152 cur += 8;
121 if (item.DeviceMinorDefined) 153
122 WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor); 154 if (item.DeviceMinor_Defined)
155 WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor)
156 else if (needDevice)
157 WRITE_OCTAL_8_CHECK (cur, 0)
123 cur += 8; 158 cur += 8;
124 159
125 if (item.IsSparse()) 160 if (!isPax && !Prefix.IsEmpty())
161 {
162 COPY_STRING_CHECK (cur, Prefix, kPrefixSize);
163 }
164
165 if (item.Is_Sparse())
126 { 166 {
127 record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0); 167 record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0);
128 WriteOctal_12(record + 483, item.Size); 168 WriteOctal_12(record + 483, item.Size);
@@ -136,31 +176,31 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
136 } 176 }
137 177
138 { 178 {
139 UInt32 checkSum = 0; 179 UInt32 sum = (unsigned)(' ') * 8; // we use binary init
140 { 180 {
141 for (unsigned i = 0; i < NFileHeader::kRecordSize; i++) 181 for (unsigned i = 0; i < kRecordSize; i++)
142 checkSum += (Byte)record[i]; 182 sum += (Byte)record[i];
143 } 183 }
144 /* we use GNU TAR scheme: 184 /* checksum field is formatted differently from the
145 checksum field is formatted differently from the
146 other fields: it has [6] digits, a null, then a space. */ 185 other fields: it has [6] digits, a null, then a space. */
147 // WRITE_OCTAL_8_CHECK(record + 148, checkSum); 186 // WRITE_OCTAL_8_CHECK(record + 148, sum);
148 const unsigned kNumDigits = 6; 187 const unsigned kNumDigits = 6;
149 for (unsigned i = 0; i < kNumDigits; i++) 188 for (unsigned i = 0; i < kNumDigits; i++)
150 { 189 {
151 record[148 + kNumDigits - 1 - i] = (char)('0' + (checkSum & 7)); 190 record[148 + kNumDigits - 1 - i] = (char)('0' + (sum & 7));
152 checkSum >>= 3; 191 sum >>= 3;
153 } 192 }
154 record[148 + 6] = 0; 193 // record[148 + 6] = 0; // we need it, if we use memset(' ') init
194 record[148 + 7] = ' '; // we need it, if we use binary init
155 } 195 }
156 196
157 RINOK(WriteBytes(record, NFileHeader::kRecordSize)); 197 RINOK(Write_Data(record, kRecordSize));
158 198
159 if (item.IsSparse()) 199 if (item.Is_Sparse())
160 { 200 {
161 for (unsigned i = 4; i < item.SparseBlocks.Size();) 201 for (unsigned i = 4; i < item.SparseBlocks.Size();)
162 { 202 {
163 memset(record, 0, NFileHeader::kRecordSize); 203 memset(record, 0, kRecordSize);
164 for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++) 204 for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++)
165 { 205 {
166 const CSparseBlock &sb = item.SparseBlocks[i]; 206 const CSparseBlock &sb = item.SparseBlocks[i];
@@ -169,7 +209,7 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
169 WriteOctal_12(p + 12, sb.Size); 209 WriteOctal_12(p + 12, sb.Size);
170 } 210 }
171 record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0); 211 record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0);
172 RINOK(WriteBytes(record, NFileHeader::kRecordSize)); 212 RINOK(Write_Data(record, kRecordSize));
173 } 213 }
174 } 214 }
175 215
@@ -177,101 +217,426 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
177} 217}
178 218
179 219
180/* OLD_GNU_TAR: writes short name with zero at the end 220static void AddPaxLine(AString &s, const char *name, const AString &val)
181 NEW_GNU_TAR: writes short name without zero at the end */ 221{
222 // s.Add_LF(); // for debug
223 const unsigned len = 3 + (unsigned)strlen(name) + val.Len();
224 AString n;
225 for (unsigned numDigits = 1;; numDigits++)
226 {
227 n.Empty();
228 n.Add_UInt32(numDigits + len);
229 if (numDigits == n.Len())
230 break;
231 }
232 s += n;
233 s.Add_Space();
234 s += name;
235 s += '=';
236 s += val;
237 s.Add_LF();
238}
239
240
241static void AddPaxTime(AString &s, const char *name, const CPaxTime &pt,
242 const CTimeOptions &options)
243{
244 unsigned numDigits = pt.NumDigits;
245 if (numDigits > options.NumDigitsMax)
246 numDigits = options.NumDigitsMax;
247
248 bool needNs = false;
249 UInt32 ns = 0;
250 if (numDigits != 0)
251 {
252 ns = pt.Ns;
253 // if (ns != 0) before reduction, we show all digits after digits reduction
254 needNs = (ns != 0 || options.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero);
255 UInt32 d = 1;
256 for (unsigned k = numDigits; k < 9; k++)
257 d *= 10;
258 ns /= d;
259 ns *= d;
260 }
261
262 AString v;
263 {
264 Int64 sec = pt.Sec;
265 if (pt.Sec < 0)
266 {
267 sec = -sec;
268 v += '-';
269 if (ns != 0)
270 {
271 ns = 1000*1000*1000 - ns;
272 sec--;
273 }
274 }
275 v.Add_UInt64(sec);
276 }
277
278 if (needNs)
279 {
280 AString d;
281 d.Add_UInt32(ns);
282 while (d.Len() < 9)
283 d.InsertAtFront('0');
284 // here we have precision
285 while (d.Len() > (unsigned)numDigits)
286 d.DeleteBack();
287 // GNU TAR reduces '0' digits.
288 if (options.RemoveZeroMode == k_PaxTimeMode_RemoveZero_Always)
289 while (!d.IsEmpty() && d.Back() == '0')
290 d.DeleteBack();
291
292 if (!d.IsEmpty())
293 {
294 v += '.';
295 v += d;
296 // v += "1234567009999"; // for debug
297 // for (int y = 0; y < 1000; y++) v += '8'; // for debug
298 }
299 }
300
301 AddPaxLine(s, name, v);
302}
303
304
305static void AddPax_UInt32_ifBig(AString &s, const char *name, const UInt32 &v)
306{
307 if (v > k_7_oct_digits_Val_Max)
308 {
309 AString s2;
310 s2.Add_UInt32(v);
311 AddPaxLine(s, name, s2);
312 }
313}
314
315
316/* OLD_GNU_TAR: writes name with zero at the end
317 NEW_GNU_TAR: can write name filled with all kNameSize characters */
182 318
183static const unsigned kNameSize_Max = 319static const unsigned kNameSize_Max =
184 NFileHeader::kNameSize; // NEW_GNU_TAR / 7-Zip 21.07 320 kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
185 // NFileHeader::kNameSize - 1; // OLD_GNU_TAR / old 7-Zip 321 // kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
186 322
187#define DOES_NAME_FIT_IN_FIELD(name) ((name).Len() <= kNameSize_Max) 323#define DOES_NAME_FIT_IN_FIELD(name) ((name).Len() <= kNameSize_Max)
188 324
325
189HRESULT COutArchive::WriteHeader(const CItem &item) 326HRESULT COutArchive::WriteHeader(const CItem &item)
190{ 327{
191 if (DOES_NAME_FIT_IN_FIELD(item.Name) && 328 Glob_Name.Empty();
192 DOES_NAME_FIT_IN_FIELD(item.LinkName)) 329 Prefix.Empty();
193 return WriteHeaderReal(item);
194 330
195 // here we can get all fields from main (item) or create new empty item 331 unsigned namePos = 0;
196 /* 332 bool needPathCut = false;
197 CItem mi; 333 bool allowPrefix = false;
198 mi.SetDefaultWriteFields(); 334
199 */ 335 if (!DOES_NAME_FIT_IN_FIELD(item.Name))
200 336 {
201 CItem mi = item; 337 const char *s = item.Name;
202 mi.LinkName.Empty(); 338 const char *p = s + item.Name.Len() - 1;
203 // SparseBlocks will be ignored by IsSparse() 339 for (; *p == '/' && p != s; p--)
204 // mi.SparseBlocks.Clear(); 340 {}
341 for (; p != s && p[-1] != '/'; p--)
342 {}
343 namePos = (unsigned)(p - s);
344 needPathCut = true;
345 }
205 346
206 mi.Name = NFileHeader::kLongLink; 347 if (IsPosixMode)
207 // 21.07 : we set Mode and MTime props as in GNU TAR: 348 {
208 mi.Mode = 0644; // octal 349 AString s;
209 mi.MTime = 0; 350
351 if (needPathCut)
352 {
353 const unsigned nameLen = item.Name.Len() - namePos;
354 if ( item.LinkFlag >= NLinkFlag::kNormal
355 && item.LinkFlag <= NLinkFlag::kDirectory
356 && namePos > 1
357 && nameLen != 0
358 // && IsPrefixAllowed
359 && item.IsMagic_Posix_ustar_00())
360 {
361 /* GNU TAR decoder supports prefix field, only if (magic)
362 signature matches 6-bytes "ustar\0".
363 so here we use prefix field only in posix mode with posix signature */
364
365 allowPrefix = true;
366 // allowPrefix = false; // for debug
367 if (namePos <= kPrefixSize + 1 && nameLen <= kNameSize_Max)
368 {
369 needPathCut = false;
370 /* we will set Prefix and Glob_Name later, for such conditions:
371 if (!DOES_NAME_FIT_IN_FIELD(item.Name) && !needPathCut) */
372 }
373 }
210 374
211 for (int i = 0; i < 2; i++) 375 if (needPathCut)
376 AddPaxLine(s, "path", item.Name);
377 }
378
379 // AddPaxLine(s, "testname", AString("testval")); // for debug
380
381 if (item.LinkName.Len() > kNameSize_Max)
382 AddPaxLine(s, "linkpath", item.LinkName);
383
384 const UInt64 kPaxSize_Limit = ((UInt64)1 << 33);
385 // const UInt64 kPaxSize_Limit = ((UInt64)1 << 1); // for debug
386 // bool zero_PackSize = false;
387 if (item.PackSize >= kPaxSize_Limit)
388 {
389 /* GNU TAR in pax mode sets PackSize = 0 in main record, if pack_size >= 8 GiB
390 But old 7-Zip doesn't detect "size" property from pax header.
391 So we write real size (>= 8 GiB) to main record in binary format,
392 and old 7-Zip can decode size correctly */
393 // zero_PackSize = true;
394 AString v;
395 v.Add_UInt64(item.PackSize);
396 AddPaxLine(s, "size", v);
397 }
398
399 /* GNU TAR encoder can set "devmajor" / "devminor" attributes,
400 but GNU TAR decoder doesn't parse "devmajor" / "devminor" */
401 if (item.DeviceMajor_Defined)
402 AddPax_UInt32_ifBig(s, "devmajor", item.DeviceMajor);
403 if (item.DeviceMinor_Defined)
404 AddPax_UInt32_ifBig(s, "devminor", item.DeviceMinor);
405
406 AddPax_UInt32_ifBig(s, "uid", item.UID);
407 AddPax_UInt32_ifBig(s, "gid", item.GID);
408
409 const UInt64 kPax_MTime_Limit = ((UInt64)1 << 33);
410 const bool zero_MTime = (
411 item.MTime < 0 ||
412 item.MTime >= (Int64)kPax_MTime_Limit);
413
414 const CPaxTime &mtime = item.PaxTimes.MTime;
415 if (mtime.IsDefined())
416 {
417 bool needPax = false;
418 if (zero_MTime)
419 needPax = true;
420 else if (TimeOptions.NumDigitsMax > 0)
421 if (mtime.Ns != 0 ||
422 (mtime.NumDigits != 0 &&
423 TimeOptions.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero))
424 needPax = true;
425 if (needPax)
426 AddPaxTime(s, "mtime", mtime, TimeOptions);
427 }
428
429 if (item.PaxTimes.ATime.IsDefined())
430 AddPaxTime(s, "atime", item.PaxTimes.ATime, TimeOptions);
431 if (item.PaxTimes.CTime.IsDefined())
432 AddPaxTime(s, "ctime", item.PaxTimes.CTime, TimeOptions);
433
434 if (item.User.Len() > kUserNameSize)
435 AddPaxLine(s, "uname", item.User);
436 if (item.Group.Len() > kGroupNameSize)
437 AddPaxLine(s, "gname", item.Group);
438
439 /*
440 // for debug
441 AString a ("11"); for (int y = 0; y < (1 << 24); y++) AddPaxLine(s, "temp", a);
442 */
443
444 const unsigned paxSize = s.Len();
445 if (paxSize != 0)
446 {
447 CItem mi = item;
448 mi.LinkName.Empty();
449 // SparseBlocks will be ignored by Is_Sparse()
450 // mi.SparseBlocks.Clear();
451 // we use "PaxHeader/*" for compatibility with previous 7-Zip decoder
452
453 // GNU TAR writes empty for these fields;
454 mi.User.Empty();
455 mi.Group.Empty();
456 mi.UID = 0;
457 mi.GID = 0;
458
459 mi.DeviceMajor_Defined = false;
460 mi.DeviceMinor_Defined = false;
461
462 mi.Name = "PaxHeader/@PaxHeader";
463 mi.Mode = 0644; // octal
464 if (zero_MTime)
465 mi.MTime = 0;
466 mi.LinkFlag = NLinkFlag::kPax;
467 // mi.LinkFlag = 'Z'; // for debug
468 mi.PackSize = paxSize;
469 // for (unsigned y = 0; y < 1; y++) { // for debug
470 RINOK(WriteHeaderReal(mi, true)); // isPax
471 RINOK(Write_Data_And_Residual(s, paxSize));
472 // } // for debug
473 /*
474 we can send (zero_MTime) for compatibility with gnu tar output.
475 we can send (zero_MTime = false) for better compatibility with old 7-Zip
476 */
477 // return WriteHeaderReal(item);
478 /*
479 false, // isPax
480 false, // zero_PackSize
481 false); // zero_MTime
482 */
483 }
484 }
485 else // !PosixMode
486 if (!DOES_NAME_FIT_IN_FIELD(item.Name) ||
487 !DOES_NAME_FIT_IN_FIELD(item.LinkName))
212 { 488 {
213 const AString *name; 489 // here we can get all fields from main (item) or create new empty item
214 // We suppose that GNU TAR also writes item for long link before item for LongName? 490 /*
215 if (i == 0) 491 CItem mi;
492 mi.SetDefaultWriteFields();
493 */
494 CItem mi = item;
495 mi.LinkName.Empty();
496 // SparseBlocks will be ignored by Is_Sparse()
497 // mi.SparseBlocks.Clear();
498 mi.Name = kLongLink;
499 // mi.Name = "././@BAD_LONG_LINK_TEST"; // for debug
500 // 21.07 : we set Mode and MTime props as in GNU TAR:
501 mi.Mode = 0644; // octal
502 mi.MTime = 0;
503
504 mi.User.Empty();
505 mi.Group.Empty();
506 /*
507 gnu tar sets "root" for such items:
508 uid_to_uname (0, &uname);
509 gid_to_gname (0, &gname);
510 */
511 /*
512 mi.User = "root";
513 mi.Group = "root";
514 */
515 mi.UID = 0;
516 mi.GID = 0;
517 mi.DeviceMajor_Defined = false;
518 mi.DeviceMinor_Defined = false;
519
520
521 for (unsigned i = 0; i < 2; i++)
216 { 522 {
217 mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongLink; 523 const AString *name;
218 name = &item.LinkName; 524 // We suppose that GNU TAR also writes item for long link before item for LongName?
525 if (i == 0)
526 {
527 mi.LinkFlag = NLinkFlag::kGnu_LongLink;
528 name = &item.LinkName;
529 }
530 else
531 {
532 mi.LinkFlag = NLinkFlag::kGnu_LongName;
533 name = &item.Name;
534 }
535 if (DOES_NAME_FIT_IN_FIELD(*name))
536 continue;
537 // GNU TAR writes null character after NAME to file. We do same here:
538 const unsigned nameStreamSize = name->Len() + 1;
539 mi.PackSize = nameStreamSize;
540 // for (unsigned y = 0; y < 3; y++) { // for debug
541 RINOK(WriteHeaderReal(mi));
542 RINOK(Write_Data_And_Residual(name->Ptr(), nameStreamSize));
543 // }
544
545 // for debug
546 /*
547 const unsigned kSize = (1 << 29) + 16;
548 CByteBuffer buf;
549 buf.Alloc(kSize);
550 memset(buf, 0, kSize);
551 memcpy(buf, name->Ptr(), name->Len());
552 const unsigned nameStreamSize = kSize;
553 mi.PackSize = nameStreamSize;
554 // for (unsigned y = 0; y < 3; y++) { // for debug
555 RINOK(WriteHeaderReal(mi));
556 RINOK(WriteBytes(buf, nameStreamSize));
557 RINOK(FillDataResidual(nameStreamSize));
558 */
219 } 559 }
560 }
561
562 // bool fals = false; if (fals) // for debug: for bit-to-bit output compatibility with GNU TAR
563
564 if (!DOES_NAME_FIT_IN_FIELD(item.Name))
565 {
566 const unsigned nameLen = item.Name.Len() - namePos;
567 if (!needPathCut)
568 Prefix.SetFrom(item.Name, namePos - 1);
220 else 569 else
221 { 570 {
222 mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongName; 571 Glob_Name = K_PREFIX_PATH_CUT "/_pc_";
223 name = &item.Name; 572
573 if (namePos == 0)
574 Glob_Name += "root";
575 else
576 {
577 Glob_Name += "crc32/";
578 char temp[12];
579 ConvertUInt32ToHex8Digits(CrcCalc(item.Name, namePos - 1), temp);
580 Glob_Name += temp;
581 }
582
583 if (!allowPrefix || Glob_Name.Len() + 1 + nameLen <= kNameSize_Max)
584 Glob_Name.Add_Slash();
585 else
586 {
587 Prefix = Glob_Name;
588 Glob_Name.Empty();
589 }
224 } 590 }
225 if (DOES_NAME_FIT_IN_FIELD(*name)) 591 Glob_Name.AddFrom(item.Name.Ptr(namePos), nameLen);
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 } 592 }
234 593
235 // 21.07: WriteHeaderReal() writes short part of (Name) and (LinkName).
236 return WriteHeaderReal(item); 594 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} 595}
246 596
247HRESULT COutArchive::FillDataResidual(UInt64 dataSize) 597
598HRESULT COutArchive::Write_Data(const void *data, unsigned size)
248{ 599{
249 unsigned lastRecordSize = ((unsigned)dataSize & (NFileHeader::kRecordSize - 1)); 600 Pos += size;
250 if (lastRecordSize == 0) 601 return WriteStream(Stream, data, size);
602}
603
604HRESULT COutArchive::Write_AfterDataResidual(UInt64 dataSize)
605{
606 const unsigned v = ((unsigned)dataSize & (kRecordSize - 1));
607 if (v == 0)
251 return S_OK; 608 return S_OK;
252 unsigned rem = NFileHeader::kRecordSize - lastRecordSize; 609 const unsigned rem = kRecordSize - v;
253 Byte buf[NFileHeader::kRecordSize]; 610 Byte buf[kRecordSize];
254 memset(buf, 0, rem); 611 memset(buf, 0, rem);
255 return WriteBytes(buf, rem); 612 return Write_Data(buf, rem);
613}
614
615
616HRESULT COutArchive::Write_Data_And_Residual(const void *data, unsigned size)
617{
618 RINOK(Write_Data(data, size));
619 return Write_AfterDataResidual(size);
256} 620}
257 621
622
258HRESULT COutArchive::WriteFinishHeader() 623HRESULT COutArchive::WriteFinishHeader()
259{ 624{
260 Byte record[NFileHeader::kRecordSize]; 625 Byte record[kRecordSize];
261 memset(record, 0, NFileHeader::kRecordSize); 626 memset(record, 0, kRecordSize);
262 627
263 const unsigned kNumFinishRecords = 2; 628 const unsigned kNumFinishRecords = 2;
264 629
265 /* GNU TAR by default uses --blocking-factor=20 (512 * 20 = 10 KiB) 630 /* GNU TAR by default uses --blocking-factor=20 (512 * 20 = 10 KiB)
266 we also can use cluster alignment: 631 we also can use cluster alignment:
267 const unsigned numBlocks = (unsigned)(Pos / NFileHeader::kRecordSize) + kNumFinishRecords; 632 const unsigned numBlocks = (unsigned)(Pos / kRecordSize) + kNumFinishRecords;
268 const unsigned kNumClusterBlocks = (1 << 3); // 8 blocks = 4 KiB 633 const unsigned kNumClusterBlocks = (1 << 3); // 8 blocks = 4 KiB
269 const unsigned numFinishRecords = kNumFinishRecords + ((kNumClusterBlocks - numBlocks) & (kNumClusterBlocks - 1)); 634 const unsigned numFinishRecords = kNumFinishRecords + ((kNumClusterBlocks - numBlocks) & (kNumClusterBlocks - 1));
270 */ 635 */
271 636
272 for (unsigned i = 0; i < kNumFinishRecords; i++) 637 for (unsigned i = 0; i < kNumFinishRecords; i++)
273 { 638 {
274 RINOK(WriteBytes(record, NFileHeader::kRecordSize)); 639 RINOK(Write_Data(record, kRecordSize));
275 } 640 }
276 return S_OK; 641 return S_OK;
277} 642}
diff --git a/CPP/7zip/Archive/Tar/TarOut.h b/CPP/7zip/Archive/Tar/TarOut.h
index ee9b965..34af20a 100644
--- a/CPP/7zip/Archive/Tar/TarOut.h
+++ b/CPP/7zip/Archive/Tar/TarOut.h
@@ -14,21 +14,38 @@ namespace NTar {
14 14
15class COutArchive 15class COutArchive
16{ 16{
17 CMyComPtr<ISequentialOutStream> m_Stream; 17 CMyComPtr<ISequentialOutStream> Stream;
18
19 AString Glob_Name;
20 AString Prefix;
21
22 HRESULT WriteHeaderReal(const CItem &item, bool isPax = false
23 // , bool zero_PackSize = false
24 // , bool zero_MTime = false
25 );
26
27 HRESULT Write_Data(const void *data, unsigned size);
28 HRESULT Write_Data_And_Residual(const void *data, unsigned size);
18 29
19 HRESULT WriteBytes(const void *data, unsigned size);
20 HRESULT WriteHeaderReal(const CItem &item);
21public: 30public:
22 UInt64 Pos; 31 UInt64 Pos;
23 32 bool IsPosixMode;
33 // bool IsPrefixAllowed; // it's used only if (IsPosixMode == true)
34 CTimeOptions TimeOptions;
35
24 void Create(ISequentialOutStream *outStream) 36 void Create(ISequentialOutStream *outStream)
25 { 37 {
26 m_Stream = outStream; 38 Stream = outStream;
27 } 39 }
28
29 HRESULT WriteHeader(const CItem &item); 40 HRESULT WriteHeader(const CItem &item);
30 HRESULT FillDataResidual(UInt64 dataSize); 41 HRESULT Write_AfterDataResidual(UInt64 dataSize);
31 HRESULT WriteFinishHeader(); 42 HRESULT WriteFinishHeader();
43
44 COutArchive():
45 Pos(0),
46 IsPosixMode(false)
47 // , IsPrefixAllowed(true)
48 {}
32}; 49};
33 50
34}} 51}}
diff --git a/CPP/7zip/Archive/Tar/TarRegister.cpp b/CPP/7zip/Archive/Tar/TarRegister.cpp
index 5014f04..a78c376 100644
--- a/CPP/7zip/Archive/Tar/TarRegister.cpp
+++ b/CPP/7zip/Archive/Tar/TarRegister.cpp
@@ -15,9 +15,17 @@ REGISTER_ARC_IO(
15 "tar", "tar ova", 0, 0xEE, 15 "tar", "tar ova", 0, 0xEE,
16 k_Signature, 16 k_Signature,
17 NFileHeader::kUstarMagic_Offset, 17 NFileHeader::kUstarMagic_Offset,
18 NArcInfoFlags::kStartOpen | 18 NArcInfoFlags::kStartOpen
19 NArcInfoFlags::kSymLinks | 19 | NArcInfoFlags::kSymLinks
20 NArcInfoFlags::kHardLinks, 20 | NArcInfoFlags::kHardLinks
21 IsArc_Tar) 21 | NArcInfoFlags::kMTime
22 | NArcInfoFlags::kMTime_Default
23 // | NArcInfoTimeFlags::kCTime
24 // | NArcInfoTimeFlags::kATime
25 , TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::kWindows)
26 | TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::kUnix)
27 | TIME_PREC_TO_ARC_FLAGS_MASK (NFileTimeType::k1ns)
28 | TIME_PREC_TO_ARC_FLAGS_TIME_DEFAULT (NFileTimeType::kUnix)
29 , IsArc_Tar)
22 30
23}} 31}}
diff --git a/CPP/7zip/Archive/Tar/TarUpdate.cpp b/CPP/7zip/Archive/Tar/TarUpdate.cpp
index 295e16b..caa0a82 100644
--- a/CPP/7zip/Archive/Tar/TarUpdate.cpp
+++ b/CPP/7zip/Archive/Tar/TarUpdate.cpp
@@ -2,6 +2,8 @@
2 2
3#include "StdAfx.h" 3#include "StdAfx.h"
4 4
5// #include <stdio.h>
6
5#include "../../../Windows/TimeUtils.h" 7#include "../../../Windows/TimeUtils.h"
6 8
7#include "../../Common/LimitedStreams.h" 9#include "../../Common/LimitedStreams.h"
@@ -15,18 +17,161 @@
15namespace NArchive { 17namespace NArchive {
16namespace NTar { 18namespace NTar {
17 19
20static void FILETIME_To_PaxTime(const FILETIME &ft, CPaxTime &pt)
21{
22 UInt32 ns;
23 pt.Sec = NWindows::NTime::FileTime_To_UnixTime64_and_Quantums(ft, ns);
24 pt.Ns = ns * 100;
25 pt.NumDigits = 7;
26}
27
28
29HRESULT Prop_To_PaxTime(const NWindows::NCOM::CPropVariant &prop, CPaxTime &pt)
30{
31 pt.Clear();
32 if (prop.vt == VT_EMPTY)
33 {
34 // pt.Sec = 0;
35 return S_OK;
36 }
37 if (prop.vt != VT_FILETIME)
38 return E_INVALIDARG;
39 {
40 UInt32 ns;
41 pt.Sec = NWindows::NTime::FileTime_To_UnixTime64_and_Quantums(prop.filetime, ns);
42 ns *= 100;
43 pt.NumDigits = 7;
44 const unsigned prec = prop.wReserved1;
45 if (prec >= k_PropVar_TimePrec_Base)
46 {
47 pt.NumDigits = prec - k_PropVar_TimePrec_Base;
48 if (prop.wReserved2 < 100)
49 ns += prop.wReserved2;
50 }
51 pt.Ns = ns;
52 return S_OK;
53 }
54}
55
56
57static HRESULT GetTime(IStreamGetProp *getProp, UInt32 pid, CPaxTime &pt)
58{
59 pt.Clear();
60 NWindows::NCOM::CPropVariant prop;
61 RINOK(getProp->GetProperty(pid, &prop));
62 return Prop_To_PaxTime(prop, pt);
63}
64
65
66static HRESULT GetUser(IStreamGetProp *getProp,
67 UInt32 pidName, UInt32 pidId, AString &name, UInt32 &id,
68 UINT codePage, unsigned utfFlags)
69{
70 // printf("\nGetUser\n");
71 // we keep old values, if both GetProperty() return VT_EMPTY
72 // we clear old values, if any of GetProperty() returns non-VT_EMPTY;
73 bool isSet = false;
74 {
75 NWindows::NCOM::CPropVariant prop;
76 RINOK(getProp->GetProperty(pidId, &prop));
77 if (prop.vt == VT_UI4)
78 {
79 isSet = true;
80 id = prop.ulVal;
81 name.Empty();
82 }
83 else if (prop.vt != VT_EMPTY)
84 return E_INVALIDARG;
85 }
86 {
87 NWindows::NCOM::CPropVariant prop;
88 RINOK(getProp->GetProperty(pidName, &prop));
89 if (prop.vt == VT_BSTR)
90 {
91 const UString s = prop.bstrVal;
92 Get_AString_From_UString(s, name, codePage, utfFlags);
93 // printf("\ngetProp->GetProperty(pidName, &prop) : %s" , name.Ptr());
94 if (!isSet)
95 id = 0;
96 }
97 else if (prop.vt == VT_UI4)
98 {
99 id = prop.ulVal;
100 name.Empty();
101 }
102 else if (prop.vt != VT_EMPTY)
103 return E_INVALIDARG;
104 }
105 return S_OK;
106}
107
108
109/*
110static HRESULT GetDevice(IStreamGetProp *getProp,
111 UInt32 &majo, UInt32 &mino, bool &majo_defined, bool &mino_defined)
112{
113 NWindows::NCOM::CPropVariant prop;
114 RINOK(getProp->GetProperty(kpidDevice, &prop));
115 if (prop.vt == VT_EMPTY)
116 return S_OK;
117 if (prop.vt != VT_UI8)
118 return E_INVALIDARG;
119 {
120 printf("\nTarUpdate.cpp :: GetDevice()\n");
121 const UInt64 v = prop.uhVal.QuadPart;
122 majo = MY_dev_major(v);
123 mino = MY_dev_minor(v);
124 majo_defined = true;
125 mino_defined = true;
126 }
127 return S_OK;
128}
129*/
130
131static HRESULT GetDevice(IStreamGetProp *getProp,
132 UInt32 pid, UInt32 &id, bool &defined)
133{
134 defined = false;
135 NWindows::NCOM::CPropVariant prop;
136 RINOK(getProp->GetProperty(pid, &prop));
137 if (prop.vt == VT_EMPTY)
138 return S_OK;
139 if (prop.vt == VT_UI4)
140 {
141 id = prop.ulVal;
142 defined = true;
143 return S_OK;
144 }
145 return E_INVALIDARG;
146}
147
148
18HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, 149HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
19 const CObjectVector<NArchive::NTar::CItemEx> &inputItems, 150 const CObjectVector<NArchive::NTar::CItemEx> &inputItems,
20 const CObjectVector<CUpdateItem> &updateItems, 151 const CObjectVector<CUpdateItem> &updateItems,
21 UINT codePage, unsigned utfFlags, 152 const CUpdateOptions &options,
22 IArchiveUpdateCallback *updateCallback) 153 IArchiveUpdateCallback *updateCallback)
23{ 154{
24 COutArchive outArchive; 155 COutArchive outArchive;
25 outArchive.Create(outStream); 156 outArchive.Create(outStream);
26 outArchive.Pos = 0; 157 outArchive.Pos = 0;
158 outArchive.IsPosixMode = options.PosixMode;
159 outArchive.TimeOptions = options.TimeOptions;
27 160
28 CMyComPtr<IOutStream> outSeekStream; 161 CMyComPtr<IOutStream> outSeekStream;
29 outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream); 162 outStream->QueryInterface(IID_IOutStream, (void **)&outSeekStream);
163 if (outSeekStream)
164 {
165 /*
166 // for debug
167 Byte buf[1 << 14];
168 memset (buf, 0, sizeof(buf));
169 RINOK(outStream->Write(buf, sizeof(buf), NULL));
170 */
171 // we need real outArchive.Pos, if outSeekStream->SetSize() will be used.
172 RINOK(outSeekStream->Seek(0, STREAM_SEEK_CUR, &outArchive.Pos));
173 }
174
30 175
31 CMyComPtr<IArchiveUpdateCallbackFile> opCallback; 176 CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
32 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback); 177 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
@@ -40,7 +185,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
40 if (ui.NewData) 185 if (ui.NewData)
41 complexity += ui.Size; 186 complexity += ui.Size;
42 else 187 else
43 complexity += inputItems[(unsigned)ui.IndexInArc].GetFullSize(); 188 complexity += inputItems[(unsigned)ui.IndexInArc].Get_FullSize_Aligned();
44 } 189 }
45 190
46 RINOK(updateCallback->SetTotal(complexity)); 191 RINOK(updateCallback->SetTotal(complexity));
@@ -58,21 +203,31 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
58 203
59 complexity = 0; 204 complexity = 0;
60 205
61 for (i = 0; i < updateItems.Size(); i++) 206 // const int kNumReduceDigits = -1; // for debug
207
208 for (i = 0;; i++)
62 { 209 {
63 lps->InSize = lps->OutSize = complexity; 210 lps->InSize = lps->OutSize = complexity;
64 RINOK(lps->SetCur()); 211 RINOK(lps->SetCur());
65 212
213 if (i == updateItems.Size())
214 return outArchive.WriteFinishHeader();
215
66 const CUpdateItem &ui = updateItems[i]; 216 const CUpdateItem &ui = updateItems[i];
67 CItem item; 217 CItem item;
68 218
69 if (ui.NewProps) 219 if (ui.NewProps)
70 { 220 {
71 item.SetDefaultWriteFields(); 221 item.SetMagic_Posix(options.PosixMode);
72 item.Mode = ui.Mode;
73 item.Name = ui.Name; 222 item.Name = ui.Name;
74 item.User = ui.User; 223 item.User = ui.User;
75 item.Group = ui.Group; 224 item.Group = ui.Group;
225 item.UID = ui.UID;
226 item.GID = ui.GID;
227 item.DeviceMajor = ui.DeviceMajor;
228 item.DeviceMinor = ui.DeviceMinor;
229 item.DeviceMajor_Defined = ui.DeviceMajor_Defined;
230 item.DeviceMinor_Defined = ui.DeviceMinor_Defined;
76 231
77 if (ui.IsDir) 232 if (ui.IsDir)
78 { 233 {
@@ -81,11 +236,15 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
81 } 236 }
82 else 237 else
83 { 238 {
84 item.LinkFlag = NFileHeader::NLinkFlag::kNormal;
85 item.PackSize = ui.Size; 239 item.PackSize = ui.Size;
240 item.Set_LinkFlag_for_File(ui.Mode);
86 } 241 }
87 242
88 item.MTime = ui.MTime; 243 // 22.00
244 item.Mode = ui.Mode & ~(UInt32)MY_LIN_S_IFMT;
245 item.PaxTimes = ui.PaxTimes;
246 // item.PaxTimes.ReducePrecison(kNumReduceDigits); // for debug
247 item.MTime = ui.PaxTimes.MTime.GetSec();
89 } 248 }
90 else 249 else
91 item = inputItems[(unsigned)ui.IndexInArc]; 250 item = inputItems[(unsigned)ui.IndexInArc];
@@ -93,7 +252,8 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
93 AString symLink; 252 AString symLink;
94 if (ui.NewData || ui.NewProps) 253 if (ui.NewData || ui.NewProps)
95 { 254 {
96 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink, codePage, utfFlags, true)); 255 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidSymLink, symLink,
256 options.CodePage, options.UtfFlags, true));
97 if (!symLink.IsEmpty()) 257 if (!symLink.IsEmpty())
98 { 258 {
99 item.LinkFlag = NFileHeader::NLinkFlag::kSymLink; 259 item.LinkFlag = NFileHeader::NLinkFlag::kSymLink;
@@ -120,7 +280,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
120 } 280 }
121 else 281 else
122 { 282 {
123 HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); 283 const HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream);
124 284
125 if (res == S_FALSE) 285 if (res == S_FALSE)
126 needWrite = false; 286 needWrite = false;
@@ -128,31 +288,105 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
128 { 288 {
129 RINOK(res); 289 RINOK(res);
130 290
131 if (fileInStream) 291 if (!fileInStream)
292 {
293 item.PackSize = 0;
294 item.Size = 0;
295 }
296 else
132 { 297 {
133 CMyComPtr<IStreamGetProps> getProps; 298 CMyComPtr<IStreamGetProps> getProps;
134 fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps); 299 CMyComPtr<IStreamGetProp> getProp;
135 if (getProps) 300 fileInStream->QueryInterface(IID_IStreamGetProp, (void **)&getProp);
301 if (getProp)
302 {
303 if (options.Write_MTime.Val) RINOK(GetTime(getProp, kpidMTime, item.PaxTimes.MTime))
304 if (options.Write_ATime.Val) RINOK(GetTime(getProp, kpidATime, item.PaxTimes.ATime))
305 if (options.Write_CTime.Val) RINOK(GetTime(getProp, kpidCTime, item.PaxTimes.CTime))
306
307 if (options.PosixMode)
308 {
309 /*
310 RINOK(GetDevice(getProp, item.DeviceMajor, item.DeviceMinor,
311 item.DeviceMajor_Defined, item.DeviceMinor_Defined));
312 */
313 bool defined = false;
314 UInt32 val = 0;
315 RINOK(GetDevice(getProp, kpidDeviceMajor, val, defined));
316 if (defined)
317 {
318 item.DeviceMajor = val;
319 item.DeviceMajor_Defined = true;
320 item.DeviceMinor = 0;
321 item.DeviceMinor_Defined = false;
322 RINOK(GetDevice(getProp, kpidDeviceMinor, item.DeviceMinor, item.DeviceMinor_Defined));
323 }
324 }
325
326 RINOK(GetUser(getProp, kpidUser, kpidUserId, item.User, item.UID, options.CodePage, options.UtfFlags));
327 RINOK(GetUser(getProp, kpidGroup, kpidGroupId, item.Group, item.GID, options.CodePage, options.UtfFlags));
328
329 {
330 NWindows::NCOM::CPropVariant prop;
331 RINOK(getProp->GetProperty(kpidPosixAttrib, &prop));
332 if (prop.vt == VT_EMPTY)
333 item.Mode =
334 MY_LIN_S_IRWXO
335 | MY_LIN_S_IRWXG
336 | MY_LIN_S_IRWXU
337 | (ui.IsDir ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG);
338 else if (prop.vt != VT_UI4)
339 return E_INVALIDARG;
340 else
341 item.Mode = prop.ulVal;
342 // 21.07 : we clear high file type bits as GNU TAR.
343 item.Set_LinkFlag_for_File(item.Mode);
344 item.Mode &= ~(UInt32)MY_LIN_S_IFMT;
345 }
346
347 {
348 NWindows::NCOM::CPropVariant prop;
349 RINOK(getProp->GetProperty(kpidSize, &prop));
350 if (prop.vt != VT_UI8)
351 return E_INVALIDARG;
352 const UInt64 size = prop.uhVal.QuadPart;
353 item.PackSize = size;
354 item.Size = size;
355 }
356 /*
357 printf("\nNum digits = %d %d\n",
358 (int)item.PaxTimes.MTime.NumDigits,
359 (int)item.PaxTimes.MTime.Ns);
360 */
361 }
362 else
136 { 363 {
137 FILETIME mTime; 364 fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
138 UInt64 size2; 365 if (getProps)
139 if (getProps->GetProps(&size2, NULL, NULL, &mTime, NULL) == S_OK)
140 { 366 {
141 item.PackSize = size2; 367 FILETIME mTime, aTime, cTime;
142 item.Size = size2; 368 UInt64 size2;
143 item.MTime = NWindows::NTime::FileTimeToUnixTime64(mTime);; 369 if (getProps->GetProps(&size2,
370 options.Write_CTime.Val ? &cTime : NULL,
371 options.Write_ATime.Val ? &aTime : NULL,
372 options.Write_MTime.Val ? &mTime : NULL,
373 NULL) == S_OK)
374 {
375 item.PackSize = size2;
376 item.Size = size2;
377 if (options.Write_MTime.Val) FILETIME_To_PaxTime(mTime, item.PaxTimes.MTime);
378 if (options.Write_ATime.Val) FILETIME_To_PaxTime(aTime, item.PaxTimes.ATime);
379 if (options.Write_CTime.Val) FILETIME_To_PaxTime(cTime, item.PaxTimes.CTime);
380 }
144 } 381 }
145 } 382 }
146 } 383 }
147 else
148 {
149 item.PackSize = 0;
150 item.Size = 0;
151 }
152 384
153 { 385 {
386 // we must request kpidHardLink after updateCallback->GetStream()
154 AString hardLink; 387 AString hardLink;
155 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink, codePage, utfFlags, true)); 388 RINOK(GetPropString(updateCallback, ui.IndexInClient, kpidHardLink, hardLink,
389 options.CodePage, options.UtfFlags, true));
156 if (!hardLink.IsEmpty()) 390 if (!hardLink.IsEmpty())
157 { 391 {
158 item.LinkFlag = NFileHeader::NLinkFlag::kHardLink; 392 item.LinkFlag = NFileHeader::NLinkFlag::kHardLink;
@@ -165,37 +399,98 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
165 } 399 }
166 } 400 }
167 401
402 // item.PaxTimes.ReducePrecison(kNumReduceDigits); // for debug
403
404 if (ui.NewProps)
405 item.MTime = item.PaxTimes.MTime.GetSec();
406
168 if (needWrite) 407 if (needWrite)
169 { 408 {
170 UInt64 fileHeaderStartPos = outArchive.Pos; 409 const UInt64 headerPos = outArchive.Pos;
410 // item.PackSize = ((UInt64)1 << 33); // for debug
171 RINOK(outArchive.WriteHeader(item)); 411 RINOK(outArchive.WriteHeader(item));
172 if (fileInStream) 412 if (fileInStream)
173 { 413 {
174 RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress)); 414 for (unsigned numPasses = 0;; numPasses++)
175 outArchive.Pos += copyCoderSpec->TotalSize;
176 if (copyCoderSpec->TotalSize != item.PackSize)
177 { 415 {
416 /* we support 2 attempts to write header:
417 pass-0: main pass:
418 pass-1: additional pass, if size_of_file and size_of_header are changed */
419 if (numPasses >= 2)
420 {
421 // opRes = NArchive::NUpdate::NOperationResult::kError_FileChanged;
422 // break;
423 return E_FAIL;
424 }
425
426 const UInt64 dataPos = outArchive.Pos;
427 RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress));
428 outArchive.Pos += copyCoderSpec->TotalSize;
429 RINOK(outArchive.Write_AfterDataResidual(copyCoderSpec->TotalSize));
430
431 // if (numPasses >= 10) // for debug
432 if (copyCoderSpec->TotalSize == item.PackSize)
433 break;
434
435 if (opCallback)
436 {
437 RINOK(opCallback->ReportOperation(
438 NEventIndexType::kOutArcIndex, (UInt32)ui.IndexInClient,
439 NUpdateNotifyOp::kInFileChanged))
440 }
441
178 if (!outSeekStream) 442 if (!outSeekStream)
179 return E_FAIL; 443 return E_FAIL;
180 UInt64 backOffset = outArchive.Pos - fileHeaderStartPos; 444 const UInt64 nextPos = outArchive.Pos;
181 RINOK(outSeekStream->Seek(-(Int64)backOffset, STREAM_SEEK_CUR, NULL)); 445 RINOK(outSeekStream->Seek(-(Int64)(nextPos - headerPos), STREAM_SEEK_CUR, NULL));
182 outArchive.Pos = fileHeaderStartPos; 446 outArchive.Pos = headerPos;
183 item.PackSize = copyCoderSpec->TotalSize; 447 item.PackSize = copyCoderSpec->TotalSize;
448
184 RINOK(outArchive.WriteHeader(item)); 449 RINOK(outArchive.WriteHeader(item));
185 RINOK(outSeekStream->Seek((Int64)item.PackSize, STREAM_SEEK_CUR, NULL)); 450
186 outArchive.Pos += item.PackSize; 451 // if (numPasses >= 10) // for debug
452 if (outArchive.Pos == dataPos)
453 {
454 const UInt64 alignedSize = nextPos - dataPos;
455 if (alignedSize != 0)
456 {
457 RINOK(outSeekStream->Seek(alignedSize, STREAM_SEEK_CUR, NULL));
458 outArchive.Pos += alignedSize;
459 }
460 break;
461 }
462
463 // size of header was changed.
464 // we remove data after header and try new attempt, if required
465 CMyComPtr<IInStream> fileSeekStream;
466 fileInStream->QueryInterface(IID_IInStream, (void **)&fileSeekStream);
467 if (!fileSeekStream)
468 return E_FAIL;
469 RINOK(fileSeekStream->Seek(0, STREAM_SEEK_SET, NULL));
470 RINOK(outSeekStream->SetSize(outArchive.Pos));
471 if (item.PackSize == 0)
472 break;
187 } 473 }
188 RINOK(outArchive.FillDataResidual(item.PackSize));
189 } 474 }
190 } 475 }
191 476
192 complexity += item.PackSize; 477 complexity += item.PackSize;
478 fileInStream.Release();
193 RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); 479 RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
194 } 480 }
195 else 481 else
196 { 482 {
483 // (ui.NewData == false)
484
485 if (opCallback)
486 {
487 RINOK(opCallback->ReportOperation(
488 NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc,
489 NUpdateNotifyOp::kReplicate))
490 }
491
197 const CItemEx &existItem = inputItems[(unsigned)ui.IndexInArc]; 492 const CItemEx &existItem = inputItems[(unsigned)ui.IndexInArc];
198 UInt64 size; 493 UInt64 size, pos;
199 494
200 if (ui.NewProps) 495 if (ui.NewProps)
201 { 496 {
@@ -216,44 +511,37 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
216 item.PackSize = existItem.PackSize; 511 item.PackSize = existItem.PackSize;
217 } 512 }
218 513
219 item.DeviceMajorDefined = existItem.DeviceMajorDefined; 514 item.DeviceMajor_Defined = existItem.DeviceMajor_Defined;
220 item.DeviceMinorDefined = existItem.DeviceMinorDefined; 515 item.DeviceMinor_Defined = existItem.DeviceMinor_Defined;
221 item.DeviceMajor = existItem.DeviceMajor; 516 item.DeviceMajor = existItem.DeviceMajor;
222 item.DeviceMinor = existItem.DeviceMinor; 517 item.DeviceMinor = existItem.DeviceMinor;
223 item.UID = existItem.UID; 518 item.UID = existItem.UID;
224 item.GID = existItem.GID; 519 item.GID = existItem.GID;
225 520
226 RINOK(outArchive.WriteHeader(item)); 521 RINOK(outArchive.WriteHeader(item));
227 RINOK(inStream->Seek((Int64)existItem.GetDataPosition(), STREAM_SEEK_SET, NULL)); 522 size = existItem.Get_PackSize_Aligned();
228 size = existItem.PackSize; 523 pos = existItem.Get_DataPos();
229 } 524 }
230 else 525 else
231 { 526 {
232 RINOK(inStream->Seek((Int64)existItem.HeaderPos, STREAM_SEEK_SET, NULL)); 527 size = existItem.Get_FullSize_Aligned();
233 size = existItem.GetFullSize(); 528 pos = existItem.HeaderPos;
234 } 529 }
235
236 streamSpec->Init(size);
237 530
238 if (opCallback) 531 if (size != 0)
239 { 532 {
240 RINOK(opCallback->ReportOperation( 533 RINOK(inStream->Seek((Int64)pos, STREAM_SEEK_SET, NULL));
241 NEventIndexType::kInArcIndex, (UInt32)ui.IndexInArc, 534 streamSpec->Init(size);
242 NUpdateNotifyOp::kReplicate)) 535 // 22.00 : we copy Residual data from old archive to new archive instead of zeroing
536 RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
537 if (copyCoderSpec->TotalSize != size)
538 return E_FAIL;
539 outArchive.Pos += size;
540 // RINOK(outArchive.Write_AfterDataResidual(existItem.PackSize));
541 complexity += size;
243 } 542 }
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 } 543 }
252 } 544 }
253
254 lps->InSize = lps->OutSize = complexity;
255 RINOK(lps->SetCur());
256 return outArchive.WriteFinishHeader();
257} 545}
258 546
259}} 547}}
diff --git a/CPP/7zip/Archive/Tar/TarUpdate.h b/CPP/7zip/Archive/Tar/TarUpdate.h
index 1e3d021..ca0976d 100644
--- a/CPP/7zip/Archive/Tar/TarUpdate.h
+++ b/CPP/7zip/Archive/Tar/TarUpdate.h
@@ -15,27 +15,60 @@ struct CUpdateItem
15 int IndexInArc; 15 int IndexInArc;
16 unsigned IndexInClient; 16 unsigned IndexInClient;
17 UInt64 Size; 17 UInt64 Size;
18 Int64 MTime; 18 // Int64 MTime;
19 UInt32 Mode; 19 UInt32 Mode;
20 bool NewData; 20 bool NewData;
21 bool NewProps; 21 bool NewProps;
22 bool IsDir; 22 bool IsDir;
23 bool DeviceMajor_Defined;
24 bool DeviceMinor_Defined;
25 UInt32 UID;
26 UInt32 GID;
27 UInt32 DeviceMajor;
28 UInt32 DeviceMinor;
23 AString Name; 29 AString Name;
24 AString User; 30 AString User;
25 AString Group; 31 AString Group;
26 32
27 CUpdateItem(): Size(0), IsDir(false) {} 33 CPaxTimes PaxTimes;
34
35 CUpdateItem():
36 Size(0),
37 IsDir(false),
38 DeviceMajor_Defined(false),
39 DeviceMinor_Defined(false),
40 UID(0),
41 GID(0)
42 {}
43};
44
45
46struct CUpdateOptions
47{
48 UINT CodePage;
49 unsigned UtfFlags;
50 bool PosixMode;
51 CBoolPair Write_MTime;
52 CBoolPair Write_ATime;
53 CBoolPair Write_CTime;
54 CTimeOptions TimeOptions;
28}; 55};
29 56
57
30HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, 58HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
31 const CObjectVector<CItemEx> &inputItems, 59 const CObjectVector<CItemEx> &inputItems,
32 const CObjectVector<CUpdateItem> &updateItems, 60 const CObjectVector<CUpdateItem> &updateItems,
33 UINT codePage, unsigned utfFlags, 61 const CUpdateOptions &options,
34 IArchiveUpdateCallback *updateCallback); 62 IArchiveUpdateCallback *updateCallback);
35 63
36HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res, 64HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res,
37 UINT codePage, unsigned utfFlags, bool convertSlash); 65 UINT codePage, unsigned utfFlags, bool convertSlash);
38 66
67HRESULT Prop_To_PaxTime(const NWindows::NCOM::CPropVariant &prop, CPaxTime &pt);
68
69void Get_AString_From_UString(const UString &s, AString &res,
70 UINT codePage, unsigned utfFlags);
71
39}} 72}}
40 73
41#endif 74#endif