xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/CpioHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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 static const Byte kMagicHex    = '1'; // New ASCII Format
34 static const Byte kMagicHexCrc = '2'; // New CRC Format
35 static const Byte kMagicOct    = '7'; // Portable ASCII Format
36 
37 static const char * const kName_TRAILER = "TRAILER!!!";
38 
39 static const unsigned k_BinRecord_Size = 2 + 8 * 2 + 2 * 4;
40 static const unsigned k_OctRecord_Size = 6 + 8 * 6 + 2 * 11;
41 static const unsigned k_HexRecord_Size = 6 + 13 * 8;
42 
43 static const unsigned k_RecordSize_Max = k_HexRecord_Size;
44 
45 enum EType
46 {
47   k_Type_BinLe,
48   k_Type_BinBe,
49   k_Type_Oct,
50   k_Type_Hex,
51   k_Type_HexCrc
52 };
53 
54 static const char * const k_Types[] =
55 {
56     "Binary LE"
57   , "Binary BE"
58   , "Portable ASCII"
59   , "New ASCII"
60   , "New CRC"
61 };
62 
63 struct CItem
64 {
65   UInt32 inode;
66   unsigned MainIndex_ForInode;
67   UInt32 Mode;
68   UInt32 MTime;
69   UInt32 DevMajor;
70   UInt32 DevMinor;
71   UInt64 Size;
72   AString Name;
73   UInt32 NumLinks;
74   UInt32 UID;
75   UInt32 GID;
76   UInt32 RDevMajor;
77   UInt32 RDevMinor;
78   UInt32 ChkSum;
79 
80   UInt32 AlignMask;
81   EType Type;
82 
83   UInt32 HeaderSize;
84   UInt64 HeaderPos;
85 
86   CByteBuffer Data; // for symlink
87 
88 
GetAlignedSizeNArchive::NCpio::CItem89   UInt32 GetAlignedSize(UInt32 size) const
90   {
91     return (size + AlignMask) & ~(UInt32)AlignMask;
92   }
93 
GetPackSizeNArchive::NCpio::CItem94   UInt64 GetPackSize() const
95   {
96     const UInt64 alignMask64 = AlignMask;
97     return (Size + alignMask64) & ~(UInt64)alignMask64;
98   }
99 
IsSame_inode_DevNArchive::NCpio::CItem100   bool IsSame_inode_Dev(const CItem &item) const
101   {
102     return inode == item.inode
103         && DevMajor == item.DevMajor
104         && DevMinor == item.DevMinor;
105   }
106 
IsBinNArchive::NCpio::CItem107   bool IsBin() const { return Type == k_Type_BinLe || Type == k_Type_BinBe; }
IsCrcFormatNArchive::NCpio::CItem108   bool IsCrcFormat() const { return Type == k_Type_HexCrc; }
IsDirNArchive::NCpio::CItem109   bool IsDir() const { return MY_LIN_S_ISDIR(Mode); }
Is_SymLinkNArchive::NCpio::CItem110   bool Is_SymLink() const { return MY_LIN_S_ISLNK(Mode); }
IsTrailerNArchive::NCpio::CItem111   bool IsTrailer() const { return strcmp(Name, kName_TRAILER) == 0; }
GetDataPositionNArchive::NCpio::CItem112   UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }
113 };
114 
115 
116 enum EErrorType
117 {
118   k_ErrorType_OK,
119   k_ErrorType_BadSignature,
120   k_ErrorType_Corrupted,
121   k_ErrorType_UnexpectedEnd
122 };
123 
124 
125 struct CInArchive
126 {
127   EErrorType errorType;
128   ISequentialInStream *Stream;
129   UInt64 Processed;
130   CItem item;
131 
132   HRESULT Read(void *data, size_t *size);
133   HRESULT GetNextItem();
134 };
135 
Read(void * data,size_t * size)136 HRESULT CInArchive::Read(void *data, size_t *size)
137 {
138   const HRESULT res = ReadStream(Stream, data, size);
139   Processed += *size;
140   return res;
141 }
142 
143 
CheckOctRecord(const Byte * p)144 static bool CheckOctRecord(const Byte *p)
145 {
146   for (unsigned i = 6; i < k_OctRecord_Size; i++)
147   {
148     const unsigned c = (unsigned)p[i] - '0';
149     if (c > 7)
150       return false;
151   }
152   return true;
153 }
154 
CheckHexRecord(const Byte * p)155 static bool CheckHexRecord(const Byte *p)
156 {
157   for (unsigned i = 6; i < k_HexRecord_Size; i++)
158   {
159     unsigned c = p[i];
160     c -= '0';
161     if (c > 9)
162     {
163       c -= 'A' - '0';
164       c &= ~0x20u;
165       if (c > 5)
166         return false;
167     }
168   }
169   return true;
170 }
171 
ReadHex(const Byte * p)172 static UInt32 ReadHex(const Byte *p)
173 {
174   char sz[16];
175   memcpy(sz, p, 8);
176   sz[8] = 0;
177   const char *end;
178   return ConvertHexStringToUInt32(sz, &end);
179 }
180 
ReadOct6(const Byte * p)181 static UInt32 ReadOct6(const Byte *p)
182 {
183   char sz[16];
184   memcpy(sz, p, 6);
185   sz[6] = 0;
186   const char *end;
187   return ConvertOctStringToUInt32(sz, &end);
188 }
189 
ReadOct11(const Byte * p)190 static UInt64 ReadOct11(const Byte *p)
191 {
192   char sz[16];
193   memcpy(sz, p, 11);
194   sz[11] = 0;
195   const char *end;
196   return ConvertOctStringToUInt64(sz, &end);
197 }
198 
199 
200 #define READ_HEX(    y, dest)  dest = ReadHex  (p + 6 + (y) * 8);
201 #define READ_OCT_6(  y, dest)  dest = ReadOct6 (p + 6 + (y));
202 #define READ_OCT_11( y, dest)  dest = ReadOct11(p + 6 + (y));
203 
204 #define Get32spec(p) (((UInt32)GetUi16(p) << 16) + GetUi16(p + 2))
205 #define G16(offs, v) v = GetUi16(p + (offs))
206 #define G32(offs, v) v = Get32spec(p + (offs))
207 
208 static const unsigned kNameSizeMax = 1 << 12;
209 
210 
IsArc_Cpio(const Byte * p,size_t size)211 API_FUNC_static_IsArc IsArc_Cpio(const Byte *p, size_t size)
212 {
213   if (size < k_BinRecord_Size)
214     return k_IsArc_Res_NEED_MORE;
215 
216   UInt32 namePos;
217   UInt32 nameSize;
218   UInt32 mode;
219   // UInt32 rDevMinor;
220   UInt32 rDevMajor = 0;
221 
222   if (p[0] == '0')
223   {
224     if (p[1] != '7' ||
225         p[2] != '0' ||
226         p[3] != '7' ||
227         p[4] != '0')
228       return k_IsArc_Res_NO;
229     if (p[5] == kMagicOct)
230     {
231       if (size < k_OctRecord_Size)
232         return k_IsArc_Res_NEED_MORE;
233       if (!CheckOctRecord(p))
234         return k_IsArc_Res_NO;
235       READ_OCT_6 (2 * 6, mode)
236       // READ_OCT_6 (6 * 6, rDevMinor)
237       READ_OCT_6 (7 * 6 + 11, nameSize)
238       namePos = k_OctRecord_Size;
239     }
240     else if (p[5] == kMagicHex || p[5] == kMagicHexCrc)
241     {
242       if (size < k_HexRecord_Size)
243         return k_IsArc_Res_NEED_MORE;
244       if (!CheckHexRecord(p))
245         return k_IsArc_Res_NO;
246       READ_HEX (1, mode)
247       READ_HEX (9, rDevMajor)
248       // READ_HEX (10, rDevMinor)
249       READ_HEX (11, nameSize)
250       namePos = k_HexRecord_Size;
251     }
252     else
253       return k_IsArc_Res_NO;
254   }
255   else
256   {
257     if (p[0] == kMagicBin0 && p[1] == kMagicBin1)
258     {
259       mode = GetUi16(p + 6);
260       // rDevMinor = GetUi16(p + 14);
261       nameSize = GetUi16(p + 20);
262     }
263     else if (p[0] == kMagicBin1 && p[1] == kMagicBin0)
264     {
265       mode = GetBe16(p + 6);
266       // rDevMinor = GetBe16(p + 14);
267       nameSize = GetBe16(p + 20);
268     }
269     else
270       return k_IsArc_Res_NO;
271     namePos = k_BinRecord_Size;
272   }
273 
274   if (mode >= (1 << 16))
275     return k_IsArc_Res_NO;
276 
277   /* v23.02: we have disabled rDevMinor check because real file
278      from Apple contains rDevMinor==255 by some unknown reason */
279   if (rDevMajor != 0
280       // || rDevMinor != 0
281       )
282   {
283     if (!MY_LIN_S_ISCHR(mode) &&
284         !MY_LIN_S_ISBLK(mode))
285       return k_IsArc_Res_NO;
286   }
287 
288   // nameSize must include the null byte
289   if (nameSize == 0 || nameSize > kNameSizeMax)
290     return k_IsArc_Res_NO;
291   {
292     unsigned lim = namePos + nameSize - 1;
293     if (lim >= size)
294       lim = (unsigned)size;
295     else if (p[lim] != 0)
296       return k_IsArc_Res_NO;
297     for (unsigned i = namePos; i < lim; i++)
298       if (p[i] == 0)
299         return k_IsArc_Res_NO;
300   }
301 
302   return k_IsArc_Res_YES;
303 }
304 }
305 
306 
307 #define READ_STREAM(_dest_, _size_) \
308   { size_t processed = (_size_); RINOK(Read(_dest_, &processed)); \
309 if (processed != (_size_)) { errorType = k_ErrorType_UnexpectedEnd; return S_OK; } }
310 
GetNextItem()311 HRESULT CInArchive::GetNextItem()
312 {
313   errorType = k_ErrorType_BadSignature;
314 
315   Byte p[k_RecordSize_Max];
316 
317   READ_STREAM(p, k_BinRecord_Size)
318 
319   UInt32 nameSize;
320   UInt32 namePos;
321 
322   /* we try to reduce probability of false detection,
323      so we check some fields for unuxpected values */
324 
325   if (p[0] != '0')
326   {
327          if (p[0] == kMagicBin0 && p[1] == kMagicBin1) { item.Type = k_Type_BinLe; }
328     else if (p[0] == kMagicBin1 && p[1] == kMagicBin0)
329     {
330       for (unsigned i = 2; i < k_BinRecord_Size; i += 2)
331       {
332         const Byte b = p[i];
333         p[i] = p[i + 1];
334         p[i + 1] = b;
335       }
336       item.Type = k_Type_BinBe;
337     }
338     else
339       return S_OK;
340 
341     errorType = k_ErrorType_Corrupted;
342 
343     item.AlignMask = 2 - 1;
344     item.DevMajor = 0;
345     item.RDevMajor = 0;
346     item.ChkSum = 0;
347 
348     G16(2, item.DevMinor);
349     G16(4, item.inode);
350     G16(6, item.Mode);
351     G16(8, item.UID);
352     G16(10, item.GID);
353     G16(12, item.NumLinks);
354     G16(14, item.RDevMinor);
355     G32(16, item.MTime);
356     G16(20, nameSize);
357     G32(22, item.Size);
358 
359     namePos = k_BinRecord_Size;
360   }
361   else
362   {
363     if (p[1] != '7' ||
364         p[2] != '0' ||
365         p[3] != '7' ||
366         p[4] != '0')
367       return S_OK;
368     if (p[5] == kMagicOct)
369     {
370       errorType = k_ErrorType_Corrupted;
371 
372       item.Type = k_Type_Oct;
373       READ_STREAM(p + k_BinRecord_Size, k_OctRecord_Size - k_BinRecord_Size)
374       item.AlignMask = 1 - 1;
375       item.DevMajor = 0;
376       item.RDevMajor = 0;
377       item.ChkSum = 0;
378 
379       if (!CheckOctRecord(p))
380         return S_OK;
381 
382       READ_OCT_6 (0, item.DevMinor)
383       READ_OCT_6 (1 * 6, item.inode)
384       READ_OCT_6 (2 * 6, item.Mode)
385       READ_OCT_6 (3 * 6, item.UID)
386       READ_OCT_6 (4 * 6, item.GID)
387       READ_OCT_6 (5 * 6, item.NumLinks)
388       READ_OCT_6 (6 * 6, item.RDevMinor)
389       {
390         UInt64 mTime64;
391         READ_OCT_11 (7 * 6, mTime64)
392         item.MTime = 0;
393         if (mTime64 <= (UInt32)(Int32)-1)
394           item.MTime = (UInt32)mTime64;
395       }
396       READ_OCT_6 (7 * 6 + 11, nameSize)
397       READ_OCT_11 (8 * 6 + 11, item.Size)  // ?????
398 
399       namePos = k_OctRecord_Size;
400     }
401     else
402     {
403            if (p[5] == kMagicHex)    item.Type = k_Type_Hex;
404       else if (p[5] == kMagicHexCrc) item.Type = k_Type_HexCrc;
405       else return S_OK;
406 
407       errorType = k_ErrorType_Corrupted;
408 
409       READ_STREAM(p + k_BinRecord_Size, k_HexRecord_Size - k_BinRecord_Size)
410 
411       if (!CheckHexRecord(p))
412         return S_OK;
413 
414       item.AlignMask = 4 - 1;
415       READ_HEX (0, item.inode)
416       READ_HEX (1, item.Mode)
417       READ_HEX (2, item.UID)
418       READ_HEX (3, item.GID)
419       READ_HEX (4, item.NumLinks)
420       READ_HEX (5, item.MTime)
421       READ_HEX (6, item.Size)
422       READ_HEX (7, item.DevMajor)
423       READ_HEX (8, item.DevMinor)
424       READ_HEX (9, item.RDevMajor)
425       READ_HEX (10, item.RDevMinor)
426       READ_HEX (11, nameSize)
427       READ_HEX (12, item.ChkSum)
428 
429       if (item.Type == k_Type_Hex && item.ChkSum != 0)
430         return S_OK;
431 
432       namePos = k_HexRecord_Size;
433     }
434   }
435 
436   if (item.Mode >= (1 << 16))
437     return S_OK;
438 
439   /* v23.02: we have disabled rDevMinor check because real file
440      from Apple contains rDevMinor==255 by some unknown reason */
441   if (item.RDevMajor != 0
442       // || item.RDevMinor != 0
443       )
444   {
445     if (!MY_LIN_S_ISCHR(item.Mode) &&
446         !MY_LIN_S_ISBLK(item.Mode))
447       return S_OK;
448   }
449 
450   // Size must be 0 for FIFOs and directories
451   if (item.IsDir() || MY_LIN_S_ISFIFO(item.Mode))
452     if (item.Size != 0)
453       return S_OK;
454 
455   // nameSize must include the null byte
456   if (nameSize == 0 || nameSize > kNameSizeMax)
457     return S_OK;
458   item.HeaderSize = item.GetAlignedSize(namePos + nameSize);
459   const UInt32 rem = item.HeaderSize - namePos;
460   char *s = item.Name.GetBuf(rem);
461   size_t processedSize = rem;
462   RINOK(Read(s, &processedSize))
463   if (processedSize != rem)
464   {
465     item.Name.ReleaseBuf_SetEnd(0);
466     errorType = k_ErrorType_UnexpectedEnd;
467     return S_OK;
468   }
469   bool pad_error = false;
470   for (size_t i = nameSize; i < processedSize; i++)
471     if (s[i] != 0)
472       pad_error = true;
473   item.Name.ReleaseBuf_CalcLen(nameSize);
474   if (item.Name.Len() + 1 != nameSize || pad_error)
475     return S_OK;
476   errorType = k_ErrorType_OK;
477   return S_OK;
478 }
479 
480 
481 
482 Z7_CLASS_IMP_CHandler_IInArchive_1(
483   IInArchiveGetStream
484 )
485   CObjectVector<CItem> _items;
486   CMyComPtr<IInStream> _stream;
487   UInt64 _phySize;
488   EType _type;
489   EErrorType _error;
490   bool _isArc;
491   bool _moreThanOneHardLinks_Error;
492   bool _numLinks_Error;
493   bool _pad_Error;
494   bool _symLink_Error;
495 };
496 
497 static const Byte kArcProps[] =
498 {
499   kpidSubType
500 };
501 
502 static const Byte kProps[] =
503 {
504   kpidPath,
505   kpidIsDir,
506   kpidSize,
507   kpidPackSize,
508   kpidMTime,
509   kpidPosixAttrib,
510   kpidLinks,
511   kpidINode,
512   kpidUserId,
513   kpidGroupId,
514   kpidDevMajor,
515   kpidDevMinor,
516   kpidDeviceMajor,
517   kpidDeviceMinor,
518   kpidChecksum,
519   kpidSymLink,
520   kpidStreamId, // for debug
521   kpidOffset
522 };
523 
524 IMP_IInArchive_Props
525 IMP_IInArchive_ArcProps
526 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))527 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
528 {
529   COM_TRY_BEGIN
530   NCOM::CPropVariant prop;
531   switch (propID)
532   {
533     case kpidSubType: prop = k_Types[(unsigned)_type]; break;
534     case kpidPhySize: prop = _phySize; break;
535     case kpidINode: prop = true; break;
536     case kpidErrorFlags:
537     {
538       UInt32 v = 0;
539       if (!_isArc)
540         v |= kpv_ErrorFlags_IsNotArc;
541       switch (_error)
542       {
543         case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break;
544         case k_ErrorType_Corrupted:     v |= kpv_ErrorFlags_HeadersError; break;
545         case k_ErrorType_OK:
546         case k_ErrorType_BadSignature:
547         // default:
548           break;
549       }
550       prop = v;
551       break;
552     }
553     case kpidWarningFlags:
554     {
555       UInt32 v = 0;
556       if (_moreThanOneHardLinks_Error)
557         v |= kpv_ErrorFlags_UnsupportedFeature; // kpv_ErrorFlags_HeadersError
558       if (_numLinks_Error
559           || _pad_Error
560           || _symLink_Error)
561         v |= kpv_ErrorFlags_HeadersError;
562       if (v != 0)
563         prop = v;
564       break;
565     }
566   }
567   prop.Detach(value);
568   return S_OK;
569   COM_TRY_END
570 }
571 
572 
CompareItems(const unsigned * p1,const unsigned * p2,void * param)573 static int CompareItems(const unsigned *p1, const unsigned *p2, void *param)
574 {
575   const CObjectVector<CItem> &items = *(const CObjectVector<CItem> *)param;
576   const unsigned index1 = *p1;
577   const unsigned index2 = *p2;
578   const CItem &i1 = items[index1];
579   const CItem &i2 = items[index2];
580   if (i1.DevMajor < i2.DevMajor) return -1;
581   if (i1.DevMajor > i2.DevMajor) return 1;
582   if (i1.DevMinor < i2.DevMinor) return -1;
583   if (i1.DevMinor > i2.DevMinor) return 1;
584   if (i1.inode < i2.inode) return -1;
585   if (i1.inode > i2.inode) return 1;
586   if (i1.IsDir())
587   {
588     if (!i2.IsDir())
589       return -1;
590   }
591   else if (i2.IsDir())
592     return 1;
593   return MyCompare(index1, index2);
594 }
595 
596 
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback))597 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback))
598 {
599   COM_TRY_BEGIN
600   {
601     Close();
602 
603     UInt64 endPos;
604     RINOK(InStream_AtBegin_GetSize(stream, endPos))
605     if (callback)
606     {
607       RINOK(callback->SetTotal(NULL, &endPos))
608     }
609 
610     CInArchive arc;
611 
612     arc.Stream = stream;
613     arc.Processed = 0;
614 
615     for (;;)
616     {
617       CItem &item = arc.item;
618       item.HeaderPos = arc.Processed;
619 
620       RINOK(arc.GetNextItem())
621 
622       _error = arc.errorType;
623 
624       if (_error != k_ErrorType_OK)
625       {
626         if (_error == k_ErrorType_BadSignature ||
627             _error == k_ErrorType_Corrupted)
628           arc.Processed = item.HeaderPos;
629         break;
630       }
631 
632       if (_items.IsEmpty())
633         _type = item.Type;
634       else if (_items.Back().Type != item.Type)
635       {
636         _error = k_ErrorType_Corrupted;
637         arc.Processed = item.HeaderPos;
638         break;
639       }
640 
641       if (item.IsTrailer())
642         break;
643 
644       item.MainIndex_ForInode = _items.Size();
645       _items.Add(item);
646 
647       const UInt64 dataSize = item.GetPackSize();
648       arc.Processed += dataSize;
649       if (arc.Processed > endPos)
650       {
651         _error = k_ErrorType_UnexpectedEnd;
652         break;
653       }
654 
655       if (item.Is_SymLink() && dataSize <= (1 << 12) && item.Size != 0)
656       {
657         size_t cur = (size_t)dataSize;
658         CByteBuffer buf;
659         buf.Alloc(cur);
660         RINOK(ReadStream(stream, buf, &cur))
661         if (cur != dataSize)
662         {
663           _error = k_ErrorType_UnexpectedEnd;
664           break;
665         }
666         size_t i;
667 
668         for (i = (size_t)item.Size; i < dataSize; i++)
669           if (buf[i] != 0)
670             break;
671         if (i != dataSize)
672           _pad_Error = true;
673 
674         for (i = 0; i < (size_t)item.Size; i++)
675           if (buf[i] == 0)
676             break;
677         if (i != (size_t)item.Size)
678           _symLink_Error = true;
679         else
680           _items.Back().Data.CopyFrom(buf, (size_t)item.Size);
681       }
682       else if (dataSize != 0)
683       {
684         UInt64 newPos;
685         RINOK(stream->Seek((Int64)dataSize, STREAM_SEEK_CUR, &newPos))
686         if (arc.Processed != newPos)
687           return E_FAIL;
688       }
689 
690       if (callback && (_items.Size() & 0xFFF) == 0)
691       {
692         const UInt64 numFiles = _items.Size();
693         RINOK(callback->SetCompleted(&numFiles, &item.HeaderPos))
694       }
695     }
696 
697     _phySize = arc.Processed;
698   }
699 
700   {
701     if (_error != k_ErrorType_OK)
702     {
703       // we try to reduce probability of false detection
704       if (_items.Size() == 0)
705         return S_FALSE;
706       // bin file uses small signature. So we do additional check for single item case.
707       if (_items.Size() == 1 && _items[0].IsBin())
708         return S_FALSE;
709     }
710     else
711     {
712       // Read tailing zeros.
713       // Most of cpio files use 512-bytes aligned zeros
714       // rare case: 4K/8K aligment is possible also
715       const unsigned kTailSize_MAX = 1 << 9;
716       Byte buf[kTailSize_MAX];
717 
718       unsigned pos = (unsigned)_phySize & (kTailSize_MAX - 1);
719       if (pos != 0) // use this check to support 512 bytes alignment only
720       for (;;)
721       {
722         const unsigned rem = kTailSize_MAX - pos;
723         size_t processed = rem;
724         RINOK(ReadStream(stream, buf + pos, &processed))
725         if (processed != rem)
726           break;
727         for (; pos < kTailSize_MAX && buf[pos] == 0; pos++)
728         {}
729         if (pos != kTailSize_MAX)
730           break;
731         _phySize += processed;
732         pos = 0;
733 
734         //       use break to support 512   bytes alignment zero tail
735         // don't use break to support 512*n bytes alignment zero tail
736         break;
737       }
738     }
739   }
740 
741   {
742     /* there was such cpio archive example with hard links:
743        {
744          all hard links (same dev/inode) are stored in neighboring items, and
745            (item.Size == 0)  for non last hard link items
746            (item.Size != 0)  for     last hard link item
747        }
748        but here we sort items by (dev/inode) to support cases
749        where hard links (same dev/inode) are not stored in neighboring items.
750 
751        // note: some cpio files have (numLinks == 0) ??
752     */
753 
754     CUIntVector indices;
755     {
756       const unsigned numItems = _items.Size();
757       indices.ClearAndSetSize(numItems);
758       if (numItems != 0)
759       {
760         unsigned *vals = &indices[0];
761         for (unsigned i = 0; i < numItems; i++)
762           vals[i] = i;
763         indices.Sort(CompareItems, (void *)&_items);
764       }
765     }
766 
767     /* Note: if cpio archive (maybe incorrect) contains
768        more then one non empty streams with identical inode number,
769        we want to extract all such data streams too.
770 
771        So we place items with identical inode to groups:
772        all items in group will have same MainIndex_ForInode,
773        that is index of last item in group with (Size != 0).
774        Another (non last) items in group have (Size == 0).
775        If there are another hard links with same inode number
776        after (Size != 0) item, we place them to another next group(s).
777 
778        Check it: maybe we should use single group for items
779        with identical inode instead, and ignore some extra data streams ?
780     */
781 
782     for (unsigned i = 0; i < indices.Size();)
783     {
784       unsigned k;
785       {
786         const CItem &item_Base = _items[indices[i]];
787 
788         if (item_Base.IsDir())
789         {
790           i++;
791           continue;
792         }
793 
794         if (i != 0)
795         {
796           const CItem &item_Prev = _items[indices[i - 1]];
797           if (!item_Prev.IsDir())
798             if (item_Base.IsSame_inode_Dev(item_Prev))
799               _moreThanOneHardLinks_Error = true;
800         }
801 
802         if (item_Base.Size != 0)
803         {
804           if (item_Base.NumLinks != 1)
805             _numLinks_Error = true;
806           i++;
807           continue;
808         }
809 
810         for (k = i + 1; k < indices.Size();)
811         {
812           const CItem &item = _items[indices[k]];
813           if (item.IsDir())
814             break;
815           if (!item.IsSame_inode_Dev(item_Base))
816             break;
817           k++;
818           if (item.Size != 0)
819             break;
820         }
821       }
822 
823       const unsigned numLinks = k - i;
824       for (;;)
825       {
826         CItem &item = _items[indices[i]];
827         if (item.NumLinks != numLinks)
828           _numLinks_Error = true;
829         if (++i == k)
830           break;
831         // if (item.Size == 0)
832         item.MainIndex_ForInode = indices[k - 1];
833       }
834     }
835   }
836 
837   _isArc = true;
838   _stream = stream;
839 
840   return S_OK;
841   COM_TRY_END
842 }
843 
844 
Z7_COM7F_IMF(CHandler::Close ())845 Z7_COM7F_IMF(CHandler::Close())
846 {
847   _items.Clear();
848   _stream.Release();
849   _phySize = 0;
850   _type = k_Type_BinLe;
851   _isArc = false;
852   _moreThanOneHardLinks_Error = false;
853   _numLinks_Error = false;
854   _pad_Error = false;
855   _symLink_Error = false;
856   _error = k_ErrorType_OK;
857   return S_OK;
858 }
859 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))860 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
861 {
862   *numItems = _items.Size();
863   return S_OK;
864 }
865 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))866 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
867 {
868   COM_TRY_BEGIN
869   NCOM::CPropVariant prop;
870   const CItem &item = _items[index];
871 
872   switch (propID)
873   {
874     case kpidPath:
875     {
876       UString res;
877       bool needConvert = true;
878       #ifdef _WIN32
879       // if (
880       ConvertUTF8ToUnicode(item.Name, res);
881       // )
882         needConvert = false;
883       #endif
884       if (needConvert)
885         res = MultiByteToUnicodeString(item.Name, CP_OEMCP);
886       prop = NItemName::GetOsPath(res);
887       break;
888     }
889     case kpidIsDir: prop = item.IsDir(); break;
890 
891     case kpidSize:
892       prop = (UInt64)_items[item.MainIndex_ForInode].Size;
893       break;
894 
895     case kpidPackSize:
896       prop = (UInt64)item.GetPackSize();
897       break;
898 
899     case kpidMTime:
900     {
901       if (item.MTime != 0)
902         PropVariant_SetFrom_UnixTime(prop, item.MTime);
903       break;
904     }
905     case kpidPosixAttrib: prop = item.Mode; break;
906     case kpidINode: prop = item.inode; break;
907     case kpidStreamId:
908       if (!item.IsDir())
909         prop = (UInt32)item.MainIndex_ForInode;
910       break;
911     case kpidDevMajor: prop = (UInt32)item.DevMajor; break;
912     case kpidDevMinor: prop = (UInt32)item.DevMinor; break;
913 
914     case kpidUserId: prop = item.UID; break;
915     case kpidGroupId: prop = item.GID; break;
916 
917     case kpidSymLink:
918       if (item.Is_SymLink() && item.Data.Size() != 0)
919       {
920         AString s;
921         s.SetFrom_CalcLen((const char *)(const void *)(const Byte *)item.Data, (unsigned)item.Data.Size());
922         if (s.Len() == item.Data.Size())
923         {
924           UString u;
925           bool needConvert = true;
926           #ifdef _WIN32
927             // if (
928             ConvertUTF8ToUnicode(item.Name, u);
929             // )
930             needConvert = false;
931           #endif
932           if (needConvert)
933             u = MultiByteToUnicodeString(s, CP_OEMCP);
934           prop = u;
935         }
936       }
937       break;
938 
939     case kpidLinks: prop = item.NumLinks; break;
940     case kpidDeviceMajor:
941       // if (item.RDevMajor != 0)
942         prop = (UInt32)item.RDevMajor;
943       break;
944     case kpidDeviceMinor:
945       // if (item.RDevMinor != 0)
946         prop = (UInt32)item.RDevMinor;
947       break;
948     case kpidChecksum:
949       if (item.IsCrcFormat())
950         prop = item.ChkSum;
951       break;
952     case kpidOffset: prop = item.GetDataPosition(); break;
953   }
954   prop.Detach(value);
955   return S_OK;
956   COM_TRY_END
957 }
958 
959 
960 Z7_CLASS_IMP_NOQIB_1(
961   COutStreamWithSum
962   , ISequentialOutStream
963 )
964   CMyComPtr<ISequentialOutStream> _stream;
965   UInt32 _checksum;
966   bool _calculate;
967 public:
968   void SetStream(ISequentialOutStream *stream) { _stream = stream; }
969   void ReleaseStream() { _stream.Release(); }
970   void Init(bool calculate)
971   {
972     _calculate = calculate;
973     _checksum = 0;
974   }
975   UInt32 GetChecksum() const { return _checksum; }
976 };
977 
978 
979 Z7_COM7F_IMF(COutStreamWithSum::Write(const void *data, UInt32 size, UInt32 *processedSize))
980 {
981   HRESULT result = S_OK;
982   if (_stream)
983     result = _stream->Write(data, size, &size);
984   if (processedSize)
985     *processedSize = size;
986   if (_calculate)
987   {
988     const Byte *p = (const Byte *)data;
989     const Byte *lim = p + size;
990     UInt32 sum = _checksum;
991     if (size >= 4)
992     {
993       lim -= 4 - 1;
994       do
995       {
996         sum += p[0] + p[1] + p[2] + p[3];
997         p += 4;
998       }
999       while (p < lim);
1000       lim += 4 - 1;
1001     }
1002     if (p != lim) { sum += *p++;
1003     if (p != lim) { sum += *p++;
1004     if (p != lim) { sum += *p++; }}}
1005     _checksum = sum;
1006   }
1007   return result;
1008 }
1009 
1010 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1011     Int32 testMode, IArchiveExtractCallback *extractCallback))
1012 {
1013   COM_TRY_BEGIN
1014   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1015   if (allFilesMode)
1016     numItems = _items.Size();
1017   if (numItems == 0)
1018     return S_OK;
1019   UInt64 totalSize = 0;
1020   UInt32 i;
1021   for (i = 0; i < numItems; i++)
1022   {
1023     const UInt32 index = allFilesMode ? i : indices[i];
1024     const CItem &item2 = _items[index];
1025     const CItem &item = _items[item2.MainIndex_ForInode];
1026     totalSize += item.Size;
1027   }
1028   RINOK(extractCallback->SetTotal(totalSize))
1029 
1030   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1031   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1032   lps->Init(extractCallback, false);
1033   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
1034   inStream->SetStream(_stream);
1035   CMyComPtr2_Create<ISequentialOutStream, COutStreamWithSum> outStreamSum;
1036 
1037   UInt64 total_PackSize = 0;
1038   UInt64 total_UnpackSize = 0;
1039 
1040   for (i = 0;; i++)
1041   {
1042     lps->InSize = total_PackSize;
1043     lps->OutSize = total_UnpackSize;
1044     RINOK(lps->SetCur())
1045     if (i >= numItems)
1046       break;
1047     const Int32 askMode = testMode ?
1048         NExtract::NAskMode::kTest :
1049         NExtract::NAskMode::kExtract;
1050     const UInt32 index = allFilesMode ? i : indices[i];
1051     const CItem &item2 = _items[index];
1052     const CItem &item = _items[item2.MainIndex_ForInode];
1053     {
1054       CMyComPtr<ISequentialOutStream> outStream;
1055       RINOK(extractCallback->GetStream(index, &outStream, askMode))
1056 
1057       total_PackSize += item2.GetPackSize();
1058       total_UnpackSize += item.Size;
1059 
1060       if (item2.IsDir())
1061       {
1062         RINOK(extractCallback->PrepareOperation(askMode))
1063         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
1064         continue;
1065       }
1066       if (!testMode && !outStream)
1067         continue;
1068       outStreamSum->Init(item.IsCrcFormat());
1069       outStreamSum->SetStream(outStream);
1070       RINOK(extractCallback->PrepareOperation(askMode))
1071     }
1072     RINOK(InStream_SeekSet(_stream, item.GetDataPosition()))
1073     inStream->Init(item.Size);
1074     RINOK(copyCoder.Interface()->Code(inStream, outStreamSum, NULL, NULL, lps))
1075     outStreamSum->ReleaseStream();
1076     Int32 res = NExtract::NOperationResult::kDataError;
1077     if (copyCoder->TotalSize == item.Size)
1078     {
1079       res = NExtract::NOperationResult::kOK;
1080       if (item.IsCrcFormat() && item.ChkSum != outStreamSum->GetChecksum())
1081         res = NExtract::NOperationResult::kCRCError;
1082     }
1083     RINOK(extractCallback->SetOperationResult(res))
1084   }
1085   return S_OK;
1086   COM_TRY_END
1087 }
1088 
1089 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
1090 {
1091   COM_TRY_BEGIN
1092   const CItem &item2 = _items[index];
1093   const CItem &item = _items[item2.MainIndex_ForInode];
1094   return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream);
1095   COM_TRY_END
1096 }
1097 
1098 static const Byte k_Signature[] = {
1099     5, '0', '7', '0', '7', '0',
1100     2, kMagicBin0, kMagicBin1,
1101     2, kMagicBin1, kMagicBin0 };
1102 
1103 REGISTER_ARC_I(
1104   "Cpio", "cpio", NULL, 0xED,
1105   k_Signature,
1106   0,
1107   NArcInfoFlags::kMultiSignature,
1108   IsArc_Cpio)
1109 
1110 }}
1111