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