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