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