1 // CpioHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "../../Common/ComTry.h"
8 #include "../../Common/MyLinux.h"
9 #include "../../Common/StringConvert.h"
10 #include "../../Common/StringToInt.h"
11 #include "../../Common/UTFConvert.h"
12
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/TimeUtils.h"
15
16 #include "../Common/LimitedStreams.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamUtils.h"
20
21 #include "../Compress/CopyCoder.h"
22
23 #include "Common/ItemNameUtils.h"
24
25 using namespace NWindows;
26
27 namespace NArchive {
28 namespace NCpio {
29
30 static const Byte kMagicBin0 = 0xC7;
31 static const Byte kMagicBin1 = 0x71;
32
33 // #define MAGIC_ASCII { '0', '7', '0', '7', '0' }
34
35 static const Byte kMagicHex = '1'; // New ASCII Format
36 static const Byte kMagicHexCrc = '2'; // New CRC Format
37 static const Byte kMagicOct = '7'; // Portable ASCII Format
38
39 static const char * const kName_TRAILER = "TRAILER!!!";
40
41 static const unsigned k_BinRecord_Size = 2 + 8 * 2 + 2 * 4;
42 static const unsigned k_OctRecord_Size = 6 + 8 * 6 + 2 * 11;
43 static const unsigned k_HexRecord_Size = 6 + 13 * 8;
44
45 static const unsigned k_RecordSize_Max = k_HexRecord_Size;
46
47 /*
48 struct CBinRecord
49 {
50 unsigned short c_magic;
51 short c_dev;
52 unsigned short c_ino;
53 unsigned short c_mode;
54 unsigned short c_uid;
55 unsigned short c_gid;
56 unsigned short c_nlink;
57 short c_rdev;
58 unsigned short c_mtimes[2];
59 unsigned short c_namesize;
60 unsigned short c_filesizes[2];
61 };
62
63 struct CHexRecord
64 {
65 char Magic[6];
66 char inode[8];
67 char Mode[8];
68 char UID[8];
69 char GID[8];
70 char nlink[8];
71 char mtime[8];
72 char Size[8]; // must be 0 for FIFOs and directories
73 char DevMajor[8];
74 char DevMinor[8];
75 char RDevMajor[8]; //only valid for chr and blk special files
76 char RDevMinor[8]; //only valid for chr and blk special files
77 char NameSize[8]; // count includes terminating NUL in pathname
78 char ChkSum[8]; // 0 for "new" portable format; for CRC format the sum of all the bytes in the file
79 };
80 */
81
82 enum EType
83 {
84 k_Type_BinLe,
85 k_Type_BinBe,
86 k_Type_Oct,
87 k_Type_Hex,
88 k_Type_HexCrc
89 };
90
91 static const char * const k_Types[] =
92 {
93 "Binary LE"
94 , "Binary BE"
95 , "Portable ASCII"
96 , "New ASCII"
97 , "New CRC"
98 };
99
100 struct CItem
101 {
102 AString Name;
103 UInt32 inode;
104 UInt32 Mode;
105 UInt32 UID;
106 UInt32 GID;
107 UInt64 Size;
108 UInt32 MTime;
109
110 UInt32 NumLinks;
111 UInt32 DevMajor;
112 UInt32 DevMinor;
113 UInt32 RDevMajor;
114 UInt32 RDevMinor;
115 UInt32 ChkSum;
116
117 UInt32 Align;
118 EType Type;
119
120 UInt32 HeaderSize;
121 UInt64 HeaderPos;
122
IsBinNArchive::NCpio::CItem123 bool IsBin() const { return Type == k_Type_BinLe || Type == k_Type_BinBe; }
IsCrcFormatNArchive::NCpio::CItem124 bool IsCrcFormat() const { return Type == k_Type_HexCrc; }
IsDirNArchive::NCpio::CItem125 bool IsDir() const { return MY_LIN_S_ISDIR(Mode); }
IsTrailerNArchive::NCpio::CItem126 bool IsTrailer() const { return strcmp(Name, kName_TRAILER) == 0; }
GetDataPositionNArchive::NCpio::CItem127 UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
128 };
129
130 enum EErrorType
131 {
132 k_ErrorType_OK,
133 k_ErrorType_Corrupted,
134 k_ErrorType_UnexpectedEnd,
135 };
136
137 struct CInArchive
138 {
139 ISequentialInStream *Stream;
140 UInt64 Processed;
141
142 HRESULT Read(void *data, size_t *size);
143 HRESULT GetNextItem(CItem &item, EErrorType &errorType);
144 };
145
Read(void * data,size_t * size)146 HRESULT CInArchive::Read(void *data, size_t *size)
147 {
148 HRESULT res = ReadStream(Stream, data, size);
149 Processed += *size;
150 return res;
151 }
152
ReadHex(const Byte * p,UInt32 & resVal)153 static bool ReadHex(const Byte *p, UInt32 &resVal)
154 {
155 char sz[16];
156 memcpy(sz, p, 8);
157 sz[8] = 0;
158 const char *end;
159 resVal = ConvertHexStringToUInt32(sz, &end);
160 return (unsigned)(end - sz) == 8;
161 }
162
ReadOct6(const Byte * p,UInt32 & resVal)163 static bool ReadOct6(const Byte *p, UInt32 &resVal)
164 {
165 char sz[16];
166 memcpy(sz, p, 6);
167 sz[6] = 0;
168 const char *end;
169 resVal = ConvertOctStringToUInt32(sz, &end);
170 return (unsigned)(end - sz) == 6;
171 }
172
ReadOct11(const Byte * p,UInt64 & resVal)173 static bool ReadOct11(const Byte *p, UInt64 &resVal)
174 {
175 char sz[16];
176 memcpy(sz, p, 11);
177 sz[11] = 0;
178 const char *end;
179 resVal = ConvertOctStringToUInt64(sz, &end);
180 return (unsigned)(end - sz) == 11;
181 }
182
183
184 #define READ_HEX(y) { if (!ReadHex(p2, y)) return S_OK; p2 += 8; }
185 #define READ_OCT_6(y) { if (!ReadOct6(p2, y)) return S_OK; p2 += 6; }
186 #define READ_OCT_11(y) { if (!ReadOct11(p2, y)) return S_OK; p2 += 11; }
187
GetAlignedSize(UInt32 size,UInt32 align)188 static UInt32 GetAlignedSize(UInt32 size, UInt32 align)
189 {
190 while ((size & (align - 1)) != 0)
191 size++;
192 return size;
193 }
194
Get16(const Byte * p,bool be)195 static UInt16 Get16(const Byte *p, bool be) { if (be) return GetBe16(p); return GetUi16(p); }
Get32(const Byte * p,bool be)196 static UInt32 Get32(const Byte *p, bool be) { return ((UInt32)Get16(p, be) << 16) + Get16(p + 2, be); }
197
198 #define G16(offs, v) v = Get16(p + (offs), be)
199 #define G32(offs, v) v = Get32(p + (offs), be)
200
201 static const unsigned kNameSizeMax = 1 << 12;
202
IsArc_Cpio(const Byte * p,size_t size)203 API_FUNC_static_IsArc IsArc_Cpio(const Byte *p, size_t size)
204 {
205 if (size < k_BinRecord_Size)
206 return k_IsArc_Res_NEED_MORE;
207
208 UInt32 nameSize;
209 UInt32 numLinks;
210 if (p[0] == '0')
211 {
212 if (p[1] != '7' ||
213 p[2] != '0' ||
214 p[3] != '7' ||
215 p[4] != '0')
216 return k_IsArc_Res_NO;
217 if (p[5] == '7')
218 {
219 if (size < k_OctRecord_Size)
220 return k_IsArc_Res_NEED_MORE;
221 for (int i = 6; i < k_OctRecord_Size; i++)
222 {
223 char c = p[i];
224 if (c < '0' || c > '7')
225 return k_IsArc_Res_NO;
226 }
227 ReadOct6(p + 6 * 6, numLinks);
228 ReadOct6(p + 8 * 6 + 11, nameSize);
229 }
230 else if (p[5] == '1' || p[5] == '2')
231 {
232 if (size < k_HexRecord_Size)
233 return k_IsArc_Res_NEED_MORE;
234 for (int i = 6; i < k_HexRecord_Size; i++)
235 {
236 char c = p[i];
237 if ((c < '0' || c > '9') &&
238 (c < 'A' || c > 'F') &&
239 (c < 'a' || c > 'f'))
240 return k_IsArc_Res_NO;
241 }
242 ReadHex(p + 6 + 4 * 8, numLinks);
243 ReadHex(p + 6 + 11 * 8, nameSize);
244 }
245 else
246 return k_IsArc_Res_NO;
247 }
248 else
249 {
250 UInt32 rDevMinor;
251 if (p[0] == kMagicBin0 && p[1] == kMagicBin1)
252 {
253 numLinks = GetUi16(p + 12);
254 rDevMinor = GetUi16(p + 14);
255 nameSize = GetUi16(p + 20);
256 }
257 else if (p[0] == kMagicBin1 && p[1] == kMagicBin0)
258 {
259 numLinks = GetBe16(p + 12);
260 rDevMinor = GetBe16(p + 14);
261 nameSize = GetBe16(p + 20);
262 }
263 else
264 return k_IsArc_Res_NO;
265
266 if (rDevMinor != 0)
267 return k_IsArc_Res_NO;
268 if (nameSize > (1 << 8))
269 return k_IsArc_Res_NO;
270 }
271 if (numLinks == 0 || numLinks >= (1 << 10))
272 return k_IsArc_Res_NO;
273 if (nameSize == 0 || nameSize > kNameSizeMax)
274 return k_IsArc_Res_NO;
275 return k_IsArc_Res_YES;
276 }
277 }
278
279 #define READ_STREAM(_dest_, _size_) \
280 { size_t processed = (_size_); RINOK(Read(_dest_, &processed)); \
281 if (processed != (_size_)) { errorType = k_ErrorType_UnexpectedEnd; return S_OK; } }
282
GetNextItem(CItem & item,EErrorType & errorType)283 HRESULT CInArchive::GetNextItem(CItem &item, EErrorType &errorType)
284 {
285 errorType = k_ErrorType_Corrupted;
286
287 Byte p[k_RecordSize_Max];
288
289 READ_STREAM(p, k_BinRecord_Size)
290
291 UInt32 nameSize;
292
293 if (p[0] != '0')
294 {
295 bool be;
296 if (p[0] == kMagicBin0 && p[1] == kMagicBin1) { be = false; item.Type = k_Type_BinLe; }
297 else if (p[0] == kMagicBin1 && p[1] == kMagicBin0) { be = true; item.Type = k_Type_BinBe; }
298 else return S_FALSE;
299
300 item.Align = 2;
301 item.DevMajor = 0;
302 item.RDevMajor =0;
303 item.ChkSum = 0;
304
305 G16(2, item.DevMinor);
306 G16(4, item.inode);
307 G16(6, item.Mode);
308 G16(8, item.UID);
309 G16(10, item.GID);
310 G16(12, item.NumLinks);
311 G16(14, item.RDevMinor);
312 G32(16, item.MTime);
313 G16(20, nameSize);
314 G32(22, item.Size);
315
316 /*
317 if (item.RDevMinor != 0)
318 return S_FALSE;
319 */
320
321 item.HeaderSize = GetAlignedSize(nameSize + k_BinRecord_Size, item.Align);
322 nameSize = item.HeaderSize - k_BinRecord_Size;
323 }
324 else
325 {
326 if (p[1] != '7' ||
327 p[2] != '0' ||
328 p[3] != '7' ||
329 p[4] != '0')
330 return S_FALSE;
331 if (p[5] == kMagicOct)
332 {
333 item.Type = k_Type_Oct;
334 READ_STREAM(p + k_BinRecord_Size, k_OctRecord_Size - k_BinRecord_Size)
335 item.Align = 1;
336 item.DevMajor = 0;
337 item.RDevMajor = 0;
338
339 const Byte *p2 = p + 6;
340 READ_OCT_6(item.DevMinor);
341 READ_OCT_6(item.inode);
342 READ_OCT_6(item.Mode);
343 READ_OCT_6(item.UID);
344 READ_OCT_6(item.GID);
345 READ_OCT_6(item.NumLinks);
346 READ_OCT_6(item.RDevMinor);
347 {
348 UInt64 mTime64;
349 READ_OCT_11(mTime64);
350 item.MTime = 0;
351 if (mTime64 < (UInt32)(Int32)-1)
352 item.MTime = (UInt32)mTime64;
353 }
354 READ_OCT_6(nameSize);
355 READ_OCT_11(item.Size); // ?????
356 item.HeaderSize = GetAlignedSize(nameSize + k_OctRecord_Size, item.Align);
357 nameSize = item.HeaderSize - k_OctRecord_Size;
358 }
359 else
360 {
361 if (p[5] == kMagicHex)
362 item.Type = k_Type_Hex;
363 else if (p[5] == kMagicHexCrc)
364 item.Type = k_Type_HexCrc;
365 else
366 return S_FALSE;
367
368 READ_STREAM(p + k_BinRecord_Size, k_HexRecord_Size - k_BinRecord_Size)
369
370 item.Align = 4;
371
372 const Byte *p2 = p + 6;
373 READ_HEX(item.inode);
374 READ_HEX(item.Mode);
375 READ_HEX(item.UID);
376 READ_HEX(item.GID);
377 READ_HEX(item.NumLinks);
378 READ_HEX(item.MTime);
379 {
380 UInt32 size32;
381 READ_HEX(size32);
382 item.Size = size32;
383 }
384 READ_HEX(item.DevMajor);
385 READ_HEX(item.DevMinor);
386 READ_HEX(item.RDevMajor);
387 READ_HEX(item.RDevMinor);
388 READ_HEX(nameSize);
389 READ_HEX(item.ChkSum);
390 if (nameSize >= kNameSizeMax)
391 return S_OK;
392 item.HeaderSize = GetAlignedSize(nameSize + k_HexRecord_Size, item.Align);
393 nameSize = item.HeaderSize - k_HexRecord_Size;
394 }
395 }
396 if (nameSize > kNameSizeMax)
397 return S_FALSE;
398 if (nameSize == 0 || nameSize >= kNameSizeMax)
399 return S_OK;
400 char *s = item.Name.GetBuf(nameSize);
401 size_t processedSize = nameSize;
402 RINOK(Read(s, &processedSize));
403 item.Name.ReleaseBuf_CalcLen(nameSize);
404 if (processedSize != nameSize)
405 {
406 errorType = k_ErrorType_UnexpectedEnd;
407 return S_OK;
408 }
409 errorType = k_ErrorType_OK;
410 return S_OK;
411 }
412
413 class CHandler:
414 public IInArchive,
415 public IInArchiveGetStream,
416 public CMyUnknownImp
417 {
418 CObjectVector<CItem> _items;
419 CMyComPtr<IInStream> _stream;
420 UInt64 _phySize;
421 EType _Type;
422 EErrorType _error;
423 bool _isArc;
424 public:
425 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
426 INTERFACE_IInArchive(;)
427 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
428 };
429
430 static const Byte kArcProps[] =
431 {
432 kpidSubType
433 };
434
435 static const Byte kProps[] =
436 {
437 kpidPath,
438 kpidIsDir,
439 kpidSize,
440 kpidMTime,
441 kpidPosixAttrib,
442 kpidLinks
443 };
444
445 IMP_IInArchive_Props
446 IMP_IInArchive_ArcProps
447
GetArchiveProperty(PROPID propID,PROPVARIANT * value)448 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
449 {
450 COM_TRY_BEGIN
451 NCOM::CPropVariant prop;
452 switch (propID)
453 {
454 case kpidSubType: prop = k_Types[(unsigned)_Type]; break;
455 case kpidPhySize: prop = _phySize; break;
456 case kpidErrorFlags:
457 {
458 UInt32 v = 0;
459 if (!_isArc)
460 v |= kpv_ErrorFlags_IsNotArc;
461 switch (_error)
462 {
463 case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break;
464 case k_ErrorType_Corrupted: v |= kpv_ErrorFlags_HeadersError; break;
465 }
466 prop = v;
467 break;
468 }
469 }
470 prop.Detach(value);
471 return S_OK;
472 COM_TRY_END
473 }
474
475
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback)476 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
477 {
478 COM_TRY_BEGIN
479 {
480 Close();
481
482 UInt64 endPos = 0;
483
484 RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
485 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
486 if (callback)
487 {
488 RINOK(callback->SetTotal(NULL, &endPos));
489 }
490
491 _items.Clear();
492 CInArchive arc;
493
494 arc.Stream = stream;
495 arc.Processed = 0;
496
497 for (;;)
498 {
499 CItem item;
500 item.HeaderPos = arc.Processed;
501 HRESULT result = arc.GetNextItem(item, _error);
502 if (result == S_FALSE)
503 return S_FALSE;
504 if (result != S_OK)
505 return S_FALSE;
506 if (_error != k_ErrorType_OK)
507 {
508 if (_error == k_ErrorType_Corrupted)
509 arc.Processed = item.HeaderPos;
510 break;
511 }
512 if (_items.IsEmpty())
513 _Type = item.Type;
514 else if (_items.Back().Type != item.Type)
515 {
516 _error = k_ErrorType_Corrupted;
517 arc.Processed = item.HeaderPos;
518 break;
519 }
520 if (item.IsTrailer())
521 break;
522
523 _items.Add(item);
524
525 {
526 // archive.SkipDataRecords(item.Size, item.Align);
527 UInt64 dataSize = item.Size;
528 UInt32 align = item.Align;
529 while ((dataSize & (align - 1)) != 0)
530 dataSize++;
531
532 // _error = k_ErrorType_UnexpectedEnd; break;
533
534 arc.Processed += dataSize;
535 if (arc.Processed > endPos)
536 {
537 _error = k_ErrorType_UnexpectedEnd;
538 break;
539 }
540
541 UInt64 newPostion;
542 RINOK(stream->Seek(dataSize, STREAM_SEEK_CUR, &newPostion));
543 if (arc.Processed != newPostion)
544 return E_FAIL;
545 }
546
547 if (callback && (_items.Size() & 0xFF) == 0)
548 {
549 UInt64 numFiles = _items.Size();
550 RINOK(callback->SetCompleted(&numFiles, &item.HeaderPos));
551 }
552 }
553 _phySize = arc.Processed;
554 if (_error != k_ErrorType_OK)
555 {
556 if (_items.Size() == 0)
557 return S_FALSE;
558 if (_items.Size() == 1 && _items[0].IsBin())
559 {
560 // probably it's false detected archive. So we return error
561 return S_FALSE;
562 }
563 }
564 else
565 {
566 // Read tailing zeros.
567 // Most of cpio files use 512-bytes aligned zeros
568 UInt64 pos = arc.Processed;
569 const UInt32 kTailSize_MAX = 1 << 9;
570 Byte buf[kTailSize_MAX];
571
572 UInt32 rem = (kTailSize_MAX - (UInt32)pos) & (kTailSize_MAX - 1);
573 if (rem != 0)
574 {
575 rem++; // we need to see that it's end of file
576 size_t processed = rem;
577 RINOK(ReadStream(stream, buf, &processed));
578 if (processed < rem)
579 {
580 unsigned i;
581 for (i = 0; i < processed && buf[i] == 0; i++);
582 if (i == processed)
583 _phySize += processed;
584 }
585 }
586 }
587
588 _isArc = true;
589 _stream = stream;
590 }
591 return S_OK;
592 COM_TRY_END
593 }
594
Close()595 STDMETHODIMP CHandler::Close()
596 {
597 _items.Clear();
598 _stream.Release();
599 _phySize = 0;
600 _Type = k_Type_BinLe;
601 _isArc = false;
602 _error = k_ErrorType_OK;
603 return S_OK;
604 }
605
GetNumberOfItems(UInt32 * numItems)606 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
607 {
608 *numItems = _items.Size();
609 return S_OK;
610 }
611
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)612 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
613 {
614 COM_TRY_BEGIN
615 NCOM::CPropVariant prop;
616 const CItem &item = _items[index];
617
618 switch (propID)
619 {
620 case kpidPath:
621 {
622 UString res;
623 bool needConvert = true;
624 #ifdef _WIN32
625 if (ConvertUTF8ToUnicode(item.Name, res))
626 needConvert = false;
627 #endif
628 if (needConvert)
629 res = MultiByteToUnicodeString(item.Name, CP_OEMCP);
630 prop = NItemName::GetOsPath(res);
631 break;
632 }
633 case kpidIsDir: prop = item.IsDir(); break;
634 case kpidSize:
635 case kpidPackSize:
636 prop = (UInt64)item.Size;
637 break;
638 case kpidMTime:
639 {
640 if (item.MTime != 0)
641 {
642 FILETIME utc;
643 NTime::UnixTimeToFileTime(item.MTime, utc);
644 prop = utc;
645 }
646 break;
647 }
648 case kpidPosixAttrib: prop = item.Mode; break;
649 case kpidLinks: prop = item.NumLinks; break;
650 /*
651 case kpidinode: prop = item.inode; break;
652 case kpidiChkSum: prop = item.ChkSum; break;
653 */
654 }
655 prop.Detach(value);
656 return S_OK;
657 COM_TRY_END
658 }
659
660 class COutStreamWithSum:
661 public ISequentialOutStream,
662 public CMyUnknownImp
663 {
664 CMyComPtr<ISequentialOutStream> _stream;
665 UInt64 _size;
666 UInt32 _crc;
667 bool _calculate;
668 public:
669 MY_UNKNOWN_IMP
670 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
SetStream(ISequentialOutStream * stream)671 void SetStream(ISequentialOutStream *stream) { _stream = stream; }
ReleaseStream()672 void ReleaseStream() { _stream.Release(); }
Init(bool calculate=true)673 void Init(bool calculate = true)
674 {
675 _size = 0;
676 _calculate = calculate;
677 _crc = 0;
678 }
EnableCalc(bool calculate)679 void EnableCalc(bool calculate) { _calculate = calculate; }
InitCRC()680 void InitCRC() { _crc = 0; }
GetSize() const681 UInt64 GetSize() const { return _size; }
GetCRC() const682 UInt32 GetCRC() const { return _crc; }
683 };
684
Write(const void * data,UInt32 size,UInt32 * processedSize)685 STDMETHODIMP COutStreamWithSum::Write(const void *data, UInt32 size, UInt32 *processedSize)
686 {
687 HRESULT result = S_OK;
688 if (_stream)
689 result = _stream->Write(data, size, &size);
690 if (_calculate)
691 {
692 UInt32 crc = 0;
693 for (UInt32 i = 0; i < size; i++)
694 crc += (UInt32)(((const Byte *)data)[i]);
695 _crc += crc;
696 }
697 if (processedSize)
698 *processedSize = size;
699 return result;
700 }
701
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)702 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
703 Int32 testMode, IArchiveExtractCallback *extractCallback)
704 {
705 COM_TRY_BEGIN
706 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
707 if (allFilesMode)
708 numItems = _items.Size();
709 if (numItems == 0)
710 return S_OK;
711 UInt64 totalSize = 0;
712 UInt32 i;
713 for (i = 0; i < numItems; i++)
714 totalSize += _items[allFilesMode ? i : indices[i]].Size;
715 extractCallback->SetTotal(totalSize);
716
717 UInt64 currentTotalSize = 0;
718
719 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
720 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
721
722 CLocalProgress *lps = new CLocalProgress;
723 CMyComPtr<ICompressProgressInfo> progress = lps;
724 lps->Init(extractCallback, false);
725
726 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
727 CMyComPtr<ISequentialInStream> inStream(streamSpec);
728 streamSpec->SetStream(_stream);
729
730 COutStreamWithSum *outStreamSumSpec = new COutStreamWithSum;
731 CMyComPtr<ISequentialOutStream> outStreamSum(outStreamSumSpec);
732
733 for (i = 0; i < numItems; i++)
734 {
735 lps->InSize = lps->OutSize = currentTotalSize;
736 RINOK(lps->SetCur());
737 CMyComPtr<ISequentialOutStream> outStream;
738 Int32 askMode = testMode ?
739 NExtract::NAskMode::kTest :
740 NExtract::NAskMode::kExtract;
741 Int32 index = allFilesMode ? i : indices[i];
742 const CItem &item = _items[index];
743 RINOK(extractCallback->GetStream(index, &outStream, askMode));
744 currentTotalSize += item.Size;
745 if (item.IsDir())
746 {
747 RINOK(extractCallback->PrepareOperation(askMode));
748 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
749 continue;
750 }
751 if (!testMode && !outStream)
752 continue;
753 outStreamSumSpec->Init(item.IsCrcFormat());
754 outStreamSumSpec->SetStream(outStream);
755 outStream.Release();
756
757 RINOK(extractCallback->PrepareOperation(askMode));
758 RINOK(_stream->Seek(item.GetDataPosition(), STREAM_SEEK_SET, NULL));
759 streamSpec->Init(item.Size);
760 RINOK(copyCoder->Code(inStream, outStreamSum, NULL, NULL, progress));
761 outStreamSumSpec->ReleaseStream();
762 Int32 res = NExtract::NOperationResult::kDataError;
763 if (copyCoderSpec->TotalSize == item.Size)
764 {
765 res = NExtract::NOperationResult::kOK;
766 if (item.IsCrcFormat() && item.ChkSum != outStreamSumSpec->GetCRC())
767 res = NExtract::NOperationResult::kCRCError;
768 }
769 RINOK(extractCallback->SetOperationResult(res));
770 }
771 return S_OK;
772 COM_TRY_END
773 }
774
GetStream(UInt32 index,ISequentialInStream ** stream)775 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
776 {
777 COM_TRY_BEGIN
778 const CItem &item = _items[index];
779 return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream);
780 COM_TRY_END
781 }
782
783 static const Byte k_Signature[] = {
784 5, '0', '7', '0', '7', '0',
785 2, kMagicBin0, kMagicBin1,
786 2, kMagicBin1, kMagicBin0 };
787
788 REGISTER_ARC_I(
789 "Cpio", "cpio", 0, 0xED,
790 k_Signature,
791 0,
792 NArcInfoFlags::kMultiSignature,
793 IsArc_Cpio)
794
795 }}
796