diff options
Diffstat (limited to '')
-rw-r--r-- | CPP/7zip/Archive/Tar/TarHandler.cpp | 445 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarHandler.h | 27 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarHandlerOut.cpp | 209 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarHeader.cpp | 77 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarHeader.h | 8 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarIn.cpp | 945 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarIn.h | 130 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarItem.h | 246 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarOut.cpp | 561 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarOut.h | 31 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarRegister.cpp | 16 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarUpdate.cpp | 406 | ||||
-rw-r--r-- | CPP/7zip/Archive/Tar/TarUpdate.h | 39 |
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 | ||
48 | static const Byte kArcProps[] = | 57 | static const Byte kArcProps[] = |
49 | { | 58 | { |
50 | kpidHeadersSize, | 59 | kpidHeadersSize, |
51 | kpidCodePage, | 60 | kpidCodePage, |
52 | kpidCharacts | 61 | kpidCharacts, |
62 | kpidComment | ||
53 | }; | 63 | }; |
54 | 64 | ||
65 | static const char *k_Characts_Prefix = "PREFIX"; | ||
66 | |||
55 | IMP_IInArchive_Props | 67 | IMP_IInArchive_Props |
56 | IMP_IInArchive_ArcProps | 68 | IMP_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 | ||
118 | HRESULT 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) | |||
294 | STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback) | 313 | STDMETHODIMP 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) | |||
314 | STDMETHODIMP CHandler::Close() | 334 | STDMETHODIMP 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 | |||
410 | static 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 | |||
427 | static 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 | |||
436 | static 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 | |||
448 | static 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 | |||
461 | static 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 | |||
393 | STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) | 472 | STDMETHODIMP 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 | |||
449 | HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, | 668 | HRESULT 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 | |||
732 | void CHandler::Init() | 952 | void 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 | |||
739 | STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps) | 964 | STDMETHODIMP 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; |
31 | private: | 31 | private: |
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 | ||
22 | STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) | 23 | STDMETHODIMP 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 | |||
44 | void 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 | |||
28 | HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res, | 54 | HRESULT 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 | ||
92 | static 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 | /* | ||
103 | static 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 | |||
123 | static 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 | |||
141 | static 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 | |||
73 | STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, | 184 | STDMETHODIMP 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 | /* | ||
27 | pre-POSIX.1-1988 (i.e. v7) tar header: | ||
28 | ----- | ||
29 | Link indicator: | ||
30 | '0' or 0 : Normal file | ||
31 | '1' : Hard link | ||
32 | '2' : Symbolic link | ||
33 | Some pre-POSIX.1-1988 tar implementations indicated a directory by having | ||
34 | a trailing slash (/) in the name. | ||
35 | |||
36 | Numeric values : octal with leading zeroes. | ||
37 | For historical reasons, a final NUL or space character should also be used. | ||
38 | Thus only 11 octal digits can be stored from 12 bytes field. | ||
39 | |||
40 | 2001 star : introduced a base-256 coding that is indicated by | ||
41 | setting the high-order bit of the leftmost byte of a numeric field. | ||
42 | GNU-tar and BSD-tar followed this idea. | ||
43 | |||
44 | versions of tar from before the first POSIX standard from 1988 | ||
45 | pad the values with spaces instead of zeroes. | ||
46 | |||
47 | UStar | ||
48 | ----- | ||
49 | UStar (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 | |||
57 | POSIX.1-2001/pax | ||
58 | ---- | ||
59 | format is known as extended tar format or pax format | ||
60 | vendor-tagged vendor-specific enhancements. | ||
61 | tags Defined by the POSIX standard: | ||
62 | atime, mtime, path, linkpath, uname, gname, size, uid, gid, ... | ||
63 | |||
64 | |||
65 | PAX EXTENSION | ||
66 | ----------- | ||
67 | Hard links | ||
68 | A further difference from the ustar header block is that data blocks | ||
69 | for files of typeflag 1 (hard link) may be included, | ||
70 | which means that the size field may be greater than zero. | ||
71 | Archives created by pax -o linkdata shall include these data | ||
72 | blocks with the hard links. | ||
73 | * | ||
74 | |||
75 | compatiblity | ||
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 | |||
84 | GNU TAR format | ||
85 | ============== | ||
86 | v7 - Unix V7 | ||
87 | oldgnu - GNU tar <=1.12 : writes zero in last character in name | ||
88 | gnu - GNU tar 1.13 : doesn't write zero in last character in name | ||
89 | as 7-zip 21.07 | ||
90 | ustar - POSIX.1-1988 | ||
91 | posix (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 | |||
17 | MY_NO_INLINE static bool IsBufNonZero(const void *data, size_t size); | ||
18 | MY_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 | |||
15 | namespace NArchive { | 54 | namespace NArchive { |
16 | namespace NTar { | 55 | namespace 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 | ||
44 | static bool OctalToNumber32(const char *srcString, unsigned size, UInt32 &res, bool allowEmpty = false) | 83 | static 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 | /* | ||
56 | static bool IsEmptyData(const char *buf, size_t size) | ||
57 | { | ||
58 | for (unsigned i = 0; i < size; i++) | ||
59 | if (buf[i] != 0) | ||
60 | return false; | ||
61 | return true; | ||
62 | } | ||
63 | */ | ||
64 | |||
65 | static bool IsRecordLast(const char *buf) | ||
66 | { | ||
67 | for (unsigned i = 0; i < NFileHeader::kRecordSize; i++) | ||
68 | if (buf[i] != 0) | ||
69 | return false; | ||
70 | return true; | ||
71 | } | ||
72 | |||
73 | static void ReadString(const char *s, unsigned size, AString &result) | 95 | static 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 | ||
78 | static bool ParseInt64(const char *p, Int64 &val) | 100 | static 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 | ||
92 | static bool ParseInt64_MTime(const char *p, Int64 &val) | 116 | static 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 | ||
104 | static bool ParseSize(const char *p, UInt64 &val) | 129 | static 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 | ||
144 | static 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 | ||
119 | API_FUNC_IsArc IsArc_Tar(const Byte *p2, size_t size) | 152 | API_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 | ||
145 | static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) | 179 | |
180 | HRESULT 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 | ||
355 | static HRESULT ReadDataToString(ISequentialInStream *stream, CItemEx &item, AString &s, EErrorType &error) | 422 | HRESULT 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 | |||
439 | HRESULT CArchive::ReadDataToBuffer(const CItemEx &item, | ||
440 | CTempBuffer &tb, size_t stringLimit) | ||
441 | { | ||
442 | tb.Init(); | ||
443 | UInt64 packSize = item.Get_PackSize_Aligned(); | ||
444 | if (packSize == 0) | ||
445 | return S_OK; | ||
446 | |||
447 | UInt64 pos; | ||
448 | |||
449 | { | ||
450 | size_t size = stringLimit; | ||
451 | if (size > packSize) | ||
452 | size = (size_t)packSize; | ||
453 | tb.Buffer.AllocAtLeast(size); | ||
454 | size_t processedSize = size; | ||
455 | const HRESULT res = ReadStream(SeqStream, tb.Buffer, &processedSize); | ||
456 | pos = processedSize; | ||
457 | if (processedSize != size) | ||
458 | { | ||
459 | error = k_ErrorType_UnexpectedEnd; | ||
460 | return res; | ||
461 | } | ||
462 | RINOK(res); | ||
463 | |||
464 | packSize -= size; | ||
465 | |||
466 | size_t i; | ||
467 | const Byte *p = tb.Buffer; | ||
468 | for (i = 0; i < size; i++) | ||
469 | if (p[i] == 0) | ||
470 | break; | ||
471 | if (i >= item.PackSize) | ||
472 | tb.StringSize_IsConfirmed = true; | ||
473 | if (i > item.PackSize) | ||
474 | { | ||
475 | tb.StringSize = (size_t)item.PackSize; | ||
476 | tb.IsNonZeroTail = true; | ||
477 | } | ||
478 | else | ||
479 | { | ||
480 | tb.StringSize = i; | ||
481 | if (i != size) | ||
482 | { | ||
483 | tb.StringSize_IsConfirmed = true; | ||
484 | if (IsBufNonZero(p + i, size - i)) | ||
485 | tb.IsNonZeroTail = true; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | if (packSize == 0) | ||
490 | return S_OK; | ||
491 | } | ||
492 | |||
493 | if (InStream) | ||
494 | { | ||
495 | RINOK(InStream->Seek((Int64)packSize, STREAM_SEEK_CUR, NULL)); | ||
496 | return S_OK; | ||
497 | } | ||
498 | const unsigned kBufSize = 1 << 15; | ||
499 | Buffer.AllocAtLeast(kBufSize); | ||
500 | |||
501 | do | ||
502 | { | ||
503 | if (OpenCallback) | ||
504 | { | ||
505 | RINOK(Progress(item, pos)); | ||
506 | } | ||
507 | |||
508 | unsigned size = kBufSize; | ||
509 | if (size > packSize) | ||
510 | size = (unsigned)packSize; | ||
511 | size_t processedSize = size; | ||
512 | const HRESULT res = ReadStream(SeqStream, Buffer, &processedSize); | ||
513 | if (processedSize != size) | ||
514 | { | ||
515 | error = k_ErrorType_UnexpectedEnd; | ||
516 | return res; | ||
517 | } | ||
518 | if (!tb.IsNonZeroTail) | ||
519 | { | ||
520 | if (IsBufNonZero(Buffer, size)) | ||
521 | tb.IsNonZeroTail = true; | ||
522 | } | ||
523 | packSize -= size; | ||
524 | pos += size; | ||
525 | } | ||
526 | while (packSize != 0); | ||
365 | return S_OK; | 527 | return S_OK; |
366 | } | 528 | } |
529 | |||
367 | 530 | ||
368 | static bool ParsePaxLongName(const AString &src, AString &dest) | 531 | |
532 | struct 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 | |||
573 | static 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 | |||
639 | bool 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 | ||
402 | HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, EErrorType &error) | 785 | |
786 | HRESULT 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 | |||
1059 | HRESULT 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 | |||
22 | struct 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 | |||
45 | class CArchive | ||
46 | { | ||
47 | public: | ||
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; | ||
68 | private: | ||
69 | EErrorType error; | ||
70 | |||
71 | public: | ||
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 | |||
123 | private: | ||
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); | ||
135 | public: | ||
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 | ||
21 | HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, EErrorType &error); | ||
22 | 144 | ||
23 | API_FUNC_IsArc IsArc_Tar(const Byte *p, size_t size); | 145 | API_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 | ||
13 | namespace NArchive { | 11 | namespace NArchive { |
@@ -19,50 +17,149 @@ struct CSparseBlock | |||
19 | UInt64 Size; | 17 | UInt64 Size; |
20 | }; | 18 | }; |
21 | 19 | ||
20 | |||
21 | enum EPaxTimeRemoveZeroMode | ||
22 | { | ||
23 | k_PaxTimeMode_DontRemoveZero, | ||
24 | k_PaxTimeMode_RemoveZero_if_PureSecondOnly, | ||
25 | k_PaxTimeMode_RemoveZero_Always | ||
26 | }; | ||
27 | |||
28 | struct 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 | |||
42 | struct 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 | |||
91 | struct 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 | |||
22 | struct CItem | 115 | struct 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 | ||
295 | struct 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 | ||
167 | struct CItemEx: public CItem | 319 | struct 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 @@ | |||
9 | namespace NArchive { | 13 | namespace NArchive { |
10 | namespace NTar { | 14 | namespace NTar { |
11 | 15 | ||
12 | HRESULT COutArchive::WriteBytes(const void *data, unsigned size) | 16 | using 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 | |
21 | static const UInt32 k_7_oct_digits_Val_Max = ((UInt32)1 << (7 * 3)) - 1; | ||
17 | 22 | ||
18 | static bool WriteOctal_8(char *s, UInt32 val) | 23 | static 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 | ||
31 | static void WriteBin_64bit(char *s, UInt64 val) | 39 | static 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 | ||
90 | HRESULT COutArchive::WriteHeaderReal(const CItem &item) | 103 | HRESULT 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 | 220 | static 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 | |||
241 | static 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 | |||
305 | static 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 | ||
183 | static const unsigned kNameSize_Max = | 319 | static 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 | |||
189 | HRESULT COutArchive::WriteHeader(const CItem &item) | 326 | HRESULT 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 | ||
247 | HRESULT COutArchive::FillDataResidual(UInt64 dataSize) | 597 | |
598 | HRESULT 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 | |||
604 | HRESULT 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 | |||
616 | HRESULT 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 | |||
258 | HRESULT COutArchive::WriteFinishHeader() | 623 | HRESULT 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 | ||
15 | class COutArchive | 15 | class 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); | ||
21 | public: | 30 | public: |
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 @@ | |||
15 | namespace NArchive { | 17 | namespace NArchive { |
16 | namespace NTar { | 18 | namespace NTar { |
17 | 19 | ||
20 | static 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 | |||
29 | HRESULT 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 | |||
57 | static 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 | |||
66 | static 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 | /* | ||
110 | static 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 | |||
131 | static 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 | |||
18 | HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, | 149 | HRESULT 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 | |||
46 | struct 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 | |||
30 | HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, | 58 | HRESULT 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 | ||
36 | HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res, | 64 | HRESULT 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 | ||
67 | HRESULT Prop_To_PaxTime(const NWindows::NCOM::CPropVariant &prop, CPaxTime &pt); | ||
68 | |||
69 | void Get_AString_From_UString(const UString &s, AString &res, | ||
70 | UINT codePage, unsigned utfFlags); | ||
71 | |||
39 | }} | 72 | }} |
40 | 73 | ||
41 | #endif | 74 | #endif |