xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/NtfsHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // NtfsHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 // #define SHOW_DEBUG_INFO2
7 
8 #if defined(SHOW_DEBUG_INFO) || defined(SHOW_DEBUG_INFO2)
9 #include <stdio.h>
10 #endif
11 
12 #include "../../../C/CpuArch.h"
13 
14 #include "../../Common/ComTry.h"
15 #include "../../Common/IntToString.h"
16 #include "../../Common/MyBuffer.h"
17 #include "../../Common/MyCom.h"
18 
19 #include "../../Windows/PropVariant.h"
20 #include "../../Windows/TimeUtils.h"
21 
22 #include "../Common/MethodProps.h"
23 #include "../Common/ProgressUtils.h"
24 #include "../Common/RegisterArc.h"
25 #include "../Common/StreamObjects.h"
26 #include "../Common/StreamUtils.h"
27 
28 #include "../Compress/CopyCoder.h"
29 
30 #include "Common/DummyOutStream.h"
31 
32 #ifdef SHOW_DEBUG_INFO
33 #define PRF(x) x
34 #define PRF_UTF16(x) PRF(printf("%S", x))
35 #else
36 #define PRF(x)
37 #define PRF_UTF16(x)
38 #endif
39 
40 #ifdef SHOW_DEBUG_INFO2
41 #define PRF2(x) x
42 #else
43 #define PRF2(x)
44 #endif
45 
46 #define Get16(p) GetUi16(p)
47 #define Get32(p) GetUi32(p)
48 #define Get64(p) GetUi64(p)
49 
50 #define G16(p, dest) dest = Get16(p)
51 #define G32(p, dest) dest = Get32(p)
52 #define G64(p, dest) dest = Get64(p)
53 
54 using namespace NWindows;
55 
56 namespace NArchive {
57 namespace Ntfs {
58 
59 static const wchar_t * const kVirtualFolder_System = L"[SYSTEM]";
60 static const wchar_t * const kVirtualFolder_Lost_Normal = L"[LOST]";
61 static const wchar_t * const kVirtualFolder_Lost_Deleted = L"[UNKNOWN]";
62 
63 static const unsigned kNumSysRecs = 16;
64 
65 static const unsigned kRecIndex_Volume    = 3;
66 static const unsigned kRecIndex_RootDir   = 5;
67 static const unsigned kRecIndex_BadClus   = 8;
68 static const unsigned kRecIndex_Security  = 9;
69 
70 struct CHeader
71 {
72   unsigned SectorSizeLog;
73   unsigned ClusterSizeLog;
74   unsigned MftRecordSizeLog;
75   // Byte MediaType;
76   // UInt32 NumHiddenSectors;
77   UInt64 NumSectors;
78   UInt64 NumClusters;
79   UInt64 MftCluster;
80   UInt64 SerialNumber;
81   // UInt16 SectorsPerTrack;
82   // UInt16 NumHeads;
83 
GetPhySize_ClustersNArchive::Ntfs::CHeader84   UInt64 GetPhySize_Clusters() const { return NumClusters << ClusterSizeLog; }
GetPhySize_MaxNArchive::Ntfs::CHeader85   UInt64 GetPhySize_Max() const { return (NumSectors + 1) << SectorSizeLog; }
ClusterSizeNArchive::Ntfs::CHeader86   UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; }
87   bool Parse(const Byte *p);
88 };
89 
GetLog(UInt32 num)90 static int GetLog(UInt32 num)
91 {
92   for (int i = 0; i < 31; i++)
93     if (((UInt32)1 << i) == num)
94       return i;
95   return -1;
96 }
97 
Parse(const Byte * p)98 bool CHeader::Parse(const Byte *p)
99 {
100   if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA)
101     return false;
102 
103   // int codeOffset = 0;
104   switch (p[0])
105   {
106     case 0xE9: /* codeOffset = 3 + (Int16)Get16(p + 1); */ break;
107     case 0xEB: if (p[2] != 0x90) return false; /* codeOffset = 2 + (int)(signed char)p[1]; */ break;
108     default: return false;
109   }
110   unsigned sectorsPerClusterLog;
111 
112   if (memcmp(p + 3, "NTFS    ", 8) != 0)
113     return false;
114   {
115     {
116       const int t = GetLog(Get16(p + 11));
117       if (t < 9 || t > 12)
118         return false;
119       SectorSizeLog = (unsigned)t;
120     }
121     {
122       const unsigned v = p[13];
123       if (v <= 0x80)
124       {
125         const int t = GetLog(v);
126         if (t < 0)
127           return false;
128         sectorsPerClusterLog = (unsigned)t;
129       }
130       else
131         sectorsPerClusterLog = 0x100 - v;
132       ClusterSizeLog = SectorSizeLog + sectorsPerClusterLog;
133       if (ClusterSizeLog > 30)
134         return false;
135     }
136   }
137 
138   for (int i = 14; i < 21; i++)
139     if (p[i] != 0)
140       return false;
141 
142   // F8 : a hard disk
143   // F0 : high-density 3.5-inch floppy disk
144   if (p[21] != 0xF8) // MediaType = Fixed_Disk
145     return false;
146   if (Get16(p + 22) != 0) // NumFatSectors
147     return false;
148   // G16(p + 24, SectorsPerTrack); // 63 usually
149   // G16(p + 26, NumHeads); // 255
150   // G32(p + 28, NumHiddenSectors); // 63 (XP) / 2048 (Vista and win7) / (0 on media that are not partitioned ?)
151   if (Get32(p + 32) != 0) // NumSectors32
152     return false;
153 
154   // DriveNumber = p[0x24];
155   if (p[0x25] != 0) // CurrentHead
156     return false;
157   /*
158   NTFS-HDD:   p[0x26] = 0x80
159   NTFS-FLASH: p[0x26] = 0
160   */
161   if (p[0x26] != 0x80 && p[0x26] != 0) // ExtendedBootSig
162     return false;
163   if (p[0x27] != 0) // reserved
164     return false;
165 
166   NumSectors = Get64(p + 0x28);
167   if (NumSectors >= ((UInt64)1 << (62 - SectorSizeLog)))
168     return false;
169 
170   NumClusters = NumSectors >> sectorsPerClusterLog;
171 
172   G64(p + 0x30, MftCluster);   // $MFT.
173   // G64(p + 0x38, Mft2Cluster);
174   G64(p + 0x48, SerialNumber); // $MFTMirr
175 
176   /*
177     numClusters_per_MftRecord:
178     numClusters_per_IndexBlock:
179     only low byte from 4 bytes is used. Another 3 high bytes are zeros.
180       If the number is positive (number < 0x80),
181           then it represents the number of clusters.
182       If the number is negative (number >= 0x80),
183           then the size of the file record is 2 raised to the absolute value of this number.
184           example: (0xF6 == -10) means 2^10 = 1024 bytes.
185   */
186   {
187     UInt32 numClusters_per_MftRecord;
188     G32(p + 0x40, numClusters_per_MftRecord);
189     if (numClusters_per_MftRecord >= 0x100 || numClusters_per_MftRecord == 0)
190       return false;
191     if (numClusters_per_MftRecord < 0x80)
192     {
193       const int t = GetLog(numClusters_per_MftRecord);
194       if (t < 0)
195         return false;
196       MftRecordSizeLog = (unsigned)t + ClusterSizeLog;
197     }
198     else
199       MftRecordSizeLog = 0x100 - numClusters_per_MftRecord;
200     // what exact MFT record sizes are possible and supported by Windows?
201     // do we need to change this limit here?
202     const unsigned k_MftRecordSizeLog_MAX = 12;
203     if (MftRecordSizeLog > k_MftRecordSizeLog_MAX)
204       return false;
205     if (MftRecordSizeLog < SectorSizeLog)
206       return false;
207   }
208   {
209     UInt32 numClusters_per_IndexBlock;
210     G32(p + 0x44, numClusters_per_IndexBlock);
211     return (numClusters_per_IndexBlock < 0x100);
212   }
213 }
214 
215 struct CMftRef
216 {
217   UInt64 Val;
218 
GetIndexNArchive::Ntfs::CMftRef219   UInt64 GetIndex() const { return Val & (((UInt64)1 << 48) - 1); }
GetNumberNArchive::Ntfs::CMftRef220   UInt16 GetNumber() const { return (UInt16)(Val >> 48); }
IsBaseItselfNArchive::Ntfs::CMftRef221   bool IsBaseItself() const { return Val == 0; }
222 
CMftRefNArchive::Ntfs::CMftRef223   CMftRef(): Val(0) {}
224 };
225 
226 #define ATNAME(n) ATTR_TYPE_ ## n
227 #define DEF_ATTR_TYPE(v, n) ATNAME(n) = v
228 
229 enum
230 {
231   DEF_ATTR_TYPE(0x00, UNUSED),
232   DEF_ATTR_TYPE(0x10, STANDARD_INFO),
233   DEF_ATTR_TYPE(0x20, ATTRIBUTE_LIST),
234   DEF_ATTR_TYPE(0x30, FILE_NAME),
235   DEF_ATTR_TYPE(0x40, OBJECT_ID),
236   DEF_ATTR_TYPE(0x50, SECURITY_DESCRIPTOR),
237   DEF_ATTR_TYPE(0x60, VOLUME_NAME),
238   DEF_ATTR_TYPE(0x70, VOLUME_INFO),
239   DEF_ATTR_TYPE(0x80, DATA),
240   DEF_ATTR_TYPE(0x90, INDEX_ROOT),
241   DEF_ATTR_TYPE(0xA0, INDEX_ALLOCATION),
242   DEF_ATTR_TYPE(0xB0, BITMAP),
243   DEF_ATTR_TYPE(0xC0, REPARSE_POINT),
244   DEF_ATTR_TYPE(0xD0, EA_INFO),
245   DEF_ATTR_TYPE(0xE0, EA),
246   DEF_ATTR_TYPE(0xF0, PROPERTY_SET),
247   DEF_ATTR_TYPE(0x100, LOGGED_UTILITY_STREAM),
248   DEF_ATTR_TYPE(0x1000, FIRST_USER_DEFINED_ATTRIBUTE)
249 };
250 
251 
252 /* WinXP-64:
253     Probably only one short name (dos name) per record is allowed.
254     There are no short names for hard links.
255    The pair (Win32,Dos) can be in any order.
256    Posix name can be after or before Win32 name
257 */
258 
259 // static const Byte kFileNameType_Posix     = 0; // for hard links
260 static const Byte kFileNameType_Win32     = 1; // after Dos name
261 static const Byte kFileNameType_Dos       = 2; // short name
262 static const Byte kFileNameType_Win32Dos  = 3; // short and full name are same
263 
264 struct CFileNameAttr
265 {
266   CMftRef ParentDirRef;
267 
268   // Probably these timestamps don't contain some useful timestamps. So we don't use them
269   // UInt64 CTime;
270   // UInt64 MTime;
271   // UInt64 ThisRecMTime;  // xp-64: the time of previous name change (not last name change. why?)
272   // UInt64 ATime;
273   // UInt64 AllocatedSize;
274   // UInt64 DataSize;
275   // UInt16 PackedEaSize;
276   UString2 Name;
277   UInt32 Attrib;
278   Byte NameType;
279 
IsDosNArchive::Ntfs::CFileNameAttr280   bool IsDos() const { return NameType == kFileNameType_Dos; }
IsWin32NArchive::Ntfs::CFileNameAttr281   bool IsWin32() const { return (NameType == kFileNameType_Win32); }
282 
283   bool Parse(const Byte *p, unsigned size);
284 
CFileNameAttrNArchive::Ntfs::CFileNameAttr285   CFileNameAttr():
286       Attrib(0),
287       NameType(0)
288       {}
289 };
290 
GetString(const Byte * p,const unsigned len,UString2 & res)291 static void GetString(const Byte *p, const unsigned len, UString2 &res)
292 {
293   if (len == 0 && res.IsEmpty())
294     return;
295   wchar_t *s = res.GetBuf(len);
296   unsigned i;
297   for (i = 0; i < len; i++)
298   {
299     const wchar_t c = Get16(p + i * 2);
300     if (c == 0)
301       break;
302     s[i] = c;
303   }
304   s[i] = 0;
305   res.ReleaseBuf_SetLen(i);
306 }
307 
Parse(const Byte * p,unsigned size)308 bool CFileNameAttr::Parse(const Byte *p, unsigned size)
309 {
310   if (size < 0x42)
311     return false;
312   G64(p + 0x00, ParentDirRef.Val);
313   // G64(p + 0x08, CTime);
314   // G64(p + 0x10, MTime);
315   // G64(p + 0x18, ThisRecMTime);
316   // G64(p + 0x20, ATime);
317   // G64(p + 0x28, AllocatedSize);
318   // G64(p + 0x30, DataSize);
319   G32(p + 0x38, Attrib);
320   // G16(p + 0x3C, PackedEaSize);
321   NameType = p[0x41];
322   const unsigned len = p[0x40];
323   if (0x42 + len * 2 > size)
324     return false;
325   if (len != 0)
326     GetString(p + 0x42, len, Name);
327   return true;
328 }
329 
330 struct CSiAttr
331 {
332   UInt64 CTime;
333   UInt64 MTime;
334   UInt64 ThisRecMTime;
335   UInt64 ATime;
336   UInt32 Attrib;
337 
338   /*
339   UInt32 MaxVersions;
340   UInt32 Version;
341   UInt32 ClassId;
342   UInt32 OwnerId;
343   */
344   UInt32 SecurityId; // SecurityId = 0 is possible ?
345   // UInt64 QuotaCharged;
346 
347   bool Parse(const Byte *p, unsigned size);
348 
CSiAttrNArchive::Ntfs::CSiAttr349   CSiAttr():
350       CTime(0),
351       MTime(0),
352       ThisRecMTime(0),
353       ATime(0),
354       Attrib(0),
355       SecurityId(0)
356       {}
357 };
358 
359 
Parse(const Byte * p,unsigned size)360 bool CSiAttr::Parse(const Byte *p, unsigned size)
361 {
362   if (size < 0x24)
363     return false;
364   G64(p + 0x00, CTime);
365   G64(p + 0x08, MTime);
366   G64(p + 0x10, ThisRecMTime);
367   G64(p + 0x18, ATime);
368   G32(p + 0x20, Attrib);
369   SecurityId = 0;
370   if (size >= 0x38)
371     G32(p + 0x34, SecurityId);
372   return true;
373 }
374 
375 static const UInt64 kEmptyExtent = (UInt64)(Int64)-1;
376 
377 struct CExtent
378 {
379   UInt64 Virt;
380   UInt64 Phy;
381 
IsEmptyNArchive::Ntfs::CExtent382   bool IsEmpty() const { return Phy == kEmptyExtent; }
383 };
384 
385 struct CVolInfo
386 {
387   Byte MajorVer;
388   Byte MinorVer;
389   // UInt16 Flags;
390 
391   bool Parse(const Byte *p, unsigned size);
392 };
393 
Parse(const Byte * p,unsigned size)394 bool CVolInfo::Parse(const Byte *p, unsigned size)
395 {
396   if (size < 12)
397     return false;
398   MajorVer = p[8];
399   MinorVer = p[9];
400   // Flags = Get16(p + 10);
401   return true;
402 }
403 
404 struct CAttr
405 {
406   UInt32 Type;
407 
408   Byte NonResident;
409 
410   // Non-Resident
411   Byte CompressionUnit;
412 
413   // UInt32 Len;
414   UString2 Name;
415   // UInt16 Flags;
416   // UInt16 Instance;
417   CByteBuffer Data;
418 
419   // Non-Resident
420   UInt64 LowVcn;
421   UInt64 HighVcn;
422   UInt64 AllocatedSize;
423   UInt64 Size;
424   UInt64 PackSize;
425   UInt64 InitializedSize;
426 
427   // Resident
428   // UInt16 ResidentFlags;
429 
IsCompressionUnitSupportedNArchive::Ntfs::CAttr430   bool IsCompressionUnitSupported() const { return CompressionUnit == 0 || CompressionUnit == 4; }
431 
432   UInt32 Parse(const Byte *p, unsigned size);
ParseFileNameNArchive::Ntfs::CAttr433   bool ParseFileName(CFileNameAttr &a) const { return a.Parse(Data, (unsigned)Data.Size()); }
ParseSiNArchive::Ntfs::CAttr434   bool ParseSi(CSiAttr &a) const { return a.Parse(Data, (unsigned)Data.Size()); }
ParseVolInfoNArchive::Ntfs::CAttr435   bool ParseVolInfo(CVolInfo &a) const { return a.Parse(Data, (unsigned)Data.Size()); }
436   bool ParseExtents(CRecordVector<CExtent> &extents, UInt64 numClustersMax, unsigned compressionUnit) const;
GetSizeNArchive::Ntfs::CAttr437   UInt64 GetSize() const { return NonResident ? Size : Data.Size(); }
GetPackSizeNArchive::Ntfs::CAttr438   UInt64 GetPackSize() const
439   {
440     if (!NonResident)
441       return Data.Size();
442     if (CompressionUnit != 0)
443       return PackSize;
444     return AllocatedSize;
445   }
446 };
447 
448 #define RINOZ(x) { int _tt_ = (x); if (_tt_ != 0) return _tt_; }
449 
CompareAttr(void * const * elem1,void * const * elem2,void *)450 static int CompareAttr(void *const *elem1, void *const *elem2, void *)
451 {
452   const CAttr &a1 = *(*((const CAttr *const *)elem1));
453   const CAttr &a2 = *(*((const CAttr *const *)elem2));
454   RINOZ(MyCompare(a1.Type, a2.Type))
455   if (a1.Name.IsEmpty())
456   {
457     if (!a2.Name.IsEmpty())
458       return -1;
459   }
460   else if (a2.Name.IsEmpty())
461     return 1;
462   else
463   {
464     RINOZ(a1.Name.Compare(a2.Name.GetRawPtr()))
465   }
466   return MyCompare(a1.LowVcn, a2.LowVcn);
467 }
468 
Parse(const Byte * p,unsigned size)469 UInt32 CAttr::Parse(const Byte *p, unsigned size)
470 {
471   if (size < 4)
472     return 0;
473   G32(p, Type);
474   if (Type == 0xFFFFFFFF)
475     return 8; // required size is 4, but attributes are 8 bytes aligned. So we return 8
476   if (size < 0x18)
477     return 0;
478 
479   PRF(printf(" T=%2X", Type));
480 
481   UInt32 len = Get32(p + 4);
482   PRF(printf(" L=%3d", len));
483   if (len > size)
484     return 0;
485   if ((len & 7) != 0)
486     return 0;
487   NonResident = p[8];
488   {
489     unsigned nameLen = p[9];
490     UInt32 nameOffset = Get16(p + 0x0A);
491     if (nameLen != 0)
492     {
493       if (nameOffset + nameLen * 2 > len)
494         return 0;
495       GetString(p + nameOffset, nameLen, Name);
496       PRF(printf(" N="));
497       PRF_UTF16(Name);
498     }
499   }
500 
501   // G16(p + 0x0C, Flags);
502   // G16(p + 0x0E, Instance);
503   // PRF(printf(" F=%4X", Flags));
504   // PRF(printf(" Inst=%d", Instance));
505 
506   UInt32 dataSize;
507   UInt32 offs;
508 
509   if (NonResident)
510   {
511     if (len < 0x40)
512       return 0;
513     PRF(printf(" NR"));
514     G64(p + 0x10, LowVcn);
515     G64(p + 0x18, HighVcn);
516     G64(p + 0x28, AllocatedSize);
517     G64(p + 0x30, Size);
518     G64(p + 0x38, InitializedSize);
519     G16(p + 0x20, offs);
520     CompressionUnit = p[0x22];
521 
522     PackSize = Size;
523     if (CompressionUnit != 0)
524     {
525       if (len < 0x48)
526         return 0;
527       G64(p + 0x40, PackSize);
528       PRF(printf(" PS=%I64x", PackSize));
529     }
530 
531     // PRF(printf("\n"));
532     PRF(printf(" ASize=%4I64d", AllocatedSize));
533     PRF(printf(" Size=%I64d", Size));
534     PRF(printf(" IS=%I64d", InitializedSize));
535     PRF(printf(" Low=%I64d", LowVcn));
536     PRF(printf(" High=%I64d", HighVcn));
537     PRF(printf(" CU=%d", (unsigned)CompressionUnit));
538     dataSize = len - offs;
539   }
540   else
541   {
542     if (len < 0x18)
543       return 0;
544     G32(p + 0x10, dataSize);
545     G16(p + 0x14, offs);
546     // G16(p + 0x16, ResidentFlags);
547     PRF(printf(" RES"));
548     PRF(printf(" dataSize=%3d", dataSize));
549     // PRF(printf(" ResFlags=%4X", ResidentFlags));
550   }
551 
552   if (offs > len || dataSize > len || len - dataSize < offs)
553     return 0;
554 
555   Data.CopyFrom(p + offs, dataSize);
556 
557   #ifdef SHOW_DEBUG_INFO
558   PRF(printf("  : "));
559   for (unsigned i = 0; i < Data.Size(); i++)
560   {
561     PRF(printf(" %02X", (unsigned)Data[i]));
562   }
563   #endif
564 
565   return len;
566 }
567 
568 
ParseExtents(CRecordVector<CExtent> & extents,UInt64 numClustersMax,unsigned compressionUnit) const569 bool CAttr::ParseExtents(CRecordVector<CExtent> &extents, UInt64 numClustersMax, unsigned compressionUnit) const
570 {
571   const Byte *p = Data;
572   unsigned size = (unsigned)Data.Size();
573   UInt64 vcn = LowVcn;
574   UInt64 lcn = 0;
575   const UInt64 highVcn1 = HighVcn + 1;
576 
577   if (LowVcn != extents.Back().Virt || highVcn1 > (UInt64)1 << 63)
578     return false;
579 
580   extents.DeleteBack();
581 
582   PRF2(printf("\n# ParseExtents # LowVcn = %4I64X # HighVcn = %4I64X", LowVcn, HighVcn));
583 
584   while (size > 0)
585   {
586     const unsigned b = *p++;
587     size--;
588     if (b == 0)
589       break;
590     unsigned num = b & 0xF;
591     if (num == 0 || num > 8 || num > size)
592       return false;
593 
594     UInt64 vSize = 0;
595     {
596       unsigned i = num;
597       do vSize = (vSize << 8) | p[--i]; while (i);
598     }
599     if (vSize == 0)
600       return false;
601     p += num;
602     size -= num;
603     if ((highVcn1 - vcn) < vSize)
604       return false;
605 
606     CExtent e;
607     e.Virt = vcn;
608     vcn += vSize;
609 
610     num = b >> 4;
611     if (num > 8 || num > size)
612       return false;
613 
614     if (num == 0)
615     {
616       // Sparse
617 
618       /* if Unit is compressed, it can have many Elements for each compressed Unit:
619          and last Element for unit MUST be without LCN.
620            Element 0: numCompressedClusters2, LCN_0
621            Element 1: numCompressedClusters2, LCN_1
622            ...
623            Last Element : (16 - total_clusters_in_previous_elements), no LCN
624       */
625 
626       // sparse is not allowed for (compressionUnit == 0) ? Why ?
627       if (compressionUnit == 0)
628         return false;
629 
630       e.Phy = kEmptyExtent;
631     }
632     else
633     {
634       Int64 v = (signed char)p[num - 1];
635       {
636         for (unsigned i = num - 1; i != 0;)
637           v = (v << 8) | p[--i];
638       }
639       p += num;
640       size -= num;
641       lcn = (UInt64)((Int64)lcn + v);
642       if (lcn > numClustersMax)
643         return false;
644       e.Phy = lcn;
645     }
646 
647     extents.Add(e);
648   }
649 
650   CExtent e;
651   e.Phy = kEmptyExtent;
652   e.Virt = vcn;
653   extents.Add(e);
654   return (highVcn1 == vcn);
655 }
656 
657 
658 static const UInt64 kEmptyTag = (UInt64)(Int64)-1;
659 
660 static const unsigned kNumCacheChunksLog = 1;
661 static const size_t kNumCacheChunks = (size_t)1 << kNumCacheChunksLog;
662 
663 Z7_CLASS_IMP_IInStream(
664   CInStream
665 )
666   UInt64 _virtPos;
667   UInt64 _physPos;
668   UInt64 _curRem;
669   bool _sparseMode;
670 public:
671   bool InUse;
672 private:
673   unsigned _chunkSizeLog;
674   CByteBuffer _inBuf;
675   CByteBuffer _outBuf;
676 public:
677   UInt64 Size;
678   UInt64 InitializedSize;
679   unsigned BlockSizeLog;
680   unsigned CompressionUnit;
681   CRecordVector<CExtent> Extents;
682   CMyComPtr<IInStream> Stream;
683 private:
684   UInt64 _tags[kNumCacheChunks];
685 
686   HRESULT SeekToPhys() { return InStream_SeekSet(Stream, _physPos); }
687   UInt32 GetCuSize() const { return (UInt32)1 << (BlockSizeLog + CompressionUnit); }
688 public:
689   HRESULT InitAndSeek(unsigned compressionUnit)
690   {
691     CompressionUnit = compressionUnit;
692     _chunkSizeLog = BlockSizeLog + CompressionUnit;
693     if (compressionUnit != 0)
694     {
695       UInt32 cuSize = GetCuSize();
696       _inBuf.Alloc(cuSize);
697       _outBuf.Alloc(kNumCacheChunks << _chunkSizeLog);
698     }
699     for (size_t i = 0; i < kNumCacheChunks; i++)
700       _tags[i] = kEmptyTag;
701 
702     _sparseMode = false;
703     _curRem = 0;
704     _virtPos = 0;
705     _physPos = 0;
706     const CExtent &e = Extents[0];
707     if (!e.IsEmpty())
708       _physPos = e.Phy << BlockSizeLog;
709     return SeekToPhys();
710   }
711 };
712 
713 static size_t Lznt1Dec(Byte *dest, size_t outBufLim, size_t destLen, const Byte *src, size_t srcLen)
714 {
715   size_t destSize = 0;
716   while (destSize < destLen)
717   {
718     if (srcLen < 2 || (destSize & 0xFFF) != 0)
719       break;
720     UInt32 comprSize;
721     {
722       const UInt32 v = Get16(src);
723       if (v == 0)
724         break;
725       src += 2;
726       srcLen -= 2;
727       comprSize = (v & 0xFFF) + 1;
728       if (comprSize > srcLen)
729         break;
730       srcLen -= comprSize;
731       if ((v & 0x8000) == 0)
732       {
733         if (comprSize != (1 << 12))
734           break;
735         memcpy(dest + destSize, src, comprSize);
736         src += comprSize;
737         destSize += comprSize;
738         continue;
739       }
740     }
741     {
742       if (destSize + (1 << 12) > outBufLim || (src[0] & 1) != 0)
743         return 0;
744       unsigned numDistBits = 4;
745       UInt32 sbOffset = 0;
746       UInt32 pos = 0;
747 
748       do
749       {
750         comprSize--;
751         for (UInt32 mask = src[pos++] | 0x100; mask > 1 && comprSize > 0; mask >>= 1)
752         {
753           if ((mask & 1) == 0)
754           {
755             if (sbOffset >= (1 << 12))
756               return 0;
757             dest[destSize++] = src[pos++];
758             sbOffset++;
759             comprSize--;
760           }
761           else
762           {
763             if (comprSize < 2)
764               return 0;
765             const UInt32 v = Get16(src + pos);
766             pos += 2;
767             comprSize -= 2;
768 
769             while (((sbOffset - 1) >> numDistBits) != 0)
770               numDistBits++;
771 
772             UInt32 len = (v & (0xFFFF >> numDistBits)) + 3;
773             if (sbOffset + len > (1 << 12))
774               return 0;
775             UInt32 dist = (v >> (16 - numDistBits));
776             if (dist >= sbOffset)
777               return 0;
778             const size_t offs = 1 + dist;
779             Byte *p = dest + destSize - offs;
780             destSize += len;
781             sbOffset += len;
782             const Byte *lim = p + len;
783             p[offs] = *p; ++p;
784             p[offs] = *p; ++p;
785             do
786               p[offs] = *p;
787             while (++p != lim);
788           }
789         }
790       }
791       while (comprSize > 0);
792       src += pos;
793     }
794   }
795   return destSize;
796 }
797 
798 Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
799 {
800   if (processedSize)
801     *processedSize = 0;
802   if (_virtPos >= Size)
803     return (Size == _virtPos) ? S_OK: E_FAIL;
804   if (size == 0)
805     return S_OK;
806   {
807     const UInt64 rem = Size - _virtPos;
808     if (size > rem)
809       size = (UInt32)rem;
810   }
811 
812   if (_virtPos >= InitializedSize)
813   {
814     memset((Byte *)data, 0, size);
815     _virtPos += size;
816     *processedSize = size;
817     return S_OK;
818   }
819 
820   {
821     const UInt64 rem = InitializedSize - _virtPos;
822     if (size > rem)
823       size = (UInt32)rem;
824   }
825 
826   while (_curRem == 0)
827   {
828     const UInt64 cacheTag = _virtPos >> _chunkSizeLog;
829     const size_t cacheIndex = (size_t)cacheTag & (kNumCacheChunks - 1);
830 
831     if (_tags[cacheIndex] == cacheTag)
832     {
833       const size_t chunkSize = (size_t)1 << _chunkSizeLog;
834       const size_t offset = (size_t)_virtPos & (chunkSize - 1);
835       size_t cur = chunkSize - offset;
836       if (cur > size)
837         cur = size;
838       memcpy(data, _outBuf + (cacheIndex << _chunkSizeLog) + offset, cur);
839       *processedSize = (UInt32)cur;
840       _virtPos += cur;
841       return S_OK;
842     }
843 
844     PRF2(printf("\nVirtPos = %6d", _virtPos));
845 
846     const UInt32 comprUnitSize = (UInt32)1 << CompressionUnit;
847     const UInt64 virtBlock = _virtPos >> BlockSizeLog;
848     const UInt64 virtBlock2 = virtBlock & ~((UInt64)comprUnitSize - 1);
849 
850     unsigned left = 0, right = Extents.Size();
851     for (;;)
852     {
853       unsigned mid = (left + right) / 2;
854       if (mid == left)
855         break;
856       if (virtBlock2 < Extents[mid].Virt)
857         right = mid;
858       else
859         left = mid;
860     }
861 
862     bool isCompressed = false;
863     const UInt64 virtBlock2End = virtBlock2 + comprUnitSize;
864     if (CompressionUnit != 0)
865       for (unsigned i = left; i < Extents.Size(); i++)
866       {
867         const CExtent &e = Extents[i];
868         if (e.Virt >= virtBlock2End)
869           break;
870         if (e.IsEmpty())
871         {
872           isCompressed = true;
873           break;
874         }
875       }
876 
877     unsigned i;
878     for (i = left; Extents[i + 1].Virt <= virtBlock; i++);
879 
880     _sparseMode = false;
881     if (!isCompressed)
882     {
883       const CExtent &e = Extents[i];
884       UInt64 newPos = (e.Phy << BlockSizeLog) + _virtPos - (e.Virt << BlockSizeLog);
885       if (newPos != _physPos)
886       {
887         _physPos = newPos;
888         RINOK(SeekToPhys())
889       }
890       UInt64 next = Extents[i + 1].Virt;
891       if (next > virtBlock2End)
892         next &= ~((UInt64)comprUnitSize - 1);
893       next <<= BlockSizeLog;
894       if (next > Size)
895         next = Size;
896       _curRem = next - _virtPos;
897       break;
898     }
899 
900     bool thereArePhy = false;
901 
902     for (unsigned i2 = left; i2 < Extents.Size(); i2++)
903     {
904       const CExtent &e = Extents[i2];
905       if (e.Virt >= virtBlock2End)
906         break;
907       if (!e.IsEmpty())
908       {
909         thereArePhy = true;
910         break;
911       }
912     }
913 
914     if (!thereArePhy)
915     {
916       _curRem = (Extents[i + 1].Virt << BlockSizeLog) - _virtPos;
917       _sparseMode = true;
918       break;
919     }
920 
921     size_t offs = 0;
922     UInt64 curVirt = virtBlock2;
923 
924     for (i = left; i < Extents.Size(); i++)
925     {
926       const CExtent &e = Extents[i];
927       if (e.IsEmpty())
928         break;
929       if (e.Virt >= virtBlock2End)
930         return S_FALSE;
931       const UInt64 newPos = (e.Phy + (curVirt - e.Virt)) << BlockSizeLog;
932       if (newPos != _physPos)
933       {
934         _physPos = newPos;
935         RINOK(SeekToPhys())
936       }
937       UInt64 numChunks = Extents[i + 1].Virt - curVirt;
938       if (curVirt + numChunks > virtBlock2End)
939         numChunks = virtBlock2End - curVirt;
940       const size_t compressed = (size_t)numChunks << BlockSizeLog;
941       RINOK(ReadStream_FALSE(Stream, _inBuf + offs, compressed))
942       curVirt += numChunks;
943       _physPos += compressed;
944       offs += compressed;
945     }
946 
947     const size_t destLenMax = GetCuSize();
948     size_t destLen = destLenMax;
949     const UInt64 rem = Size - (virtBlock2 << BlockSizeLog);
950     if (destLen > rem)
951       destLen = (size_t)rem;
952 
953     Byte *dest = _outBuf + (cacheIndex << _chunkSizeLog);
954     const size_t destSizeRes = Lznt1Dec(dest, destLenMax, destLen, _inBuf, offs);
955     _tags[cacheIndex] = cacheTag;
956 
957     // some files in Vista have destSize > destLen
958     if (destSizeRes < destLen)
959     {
960       memset(dest, 0, destLenMax);
961       if (InUse)
962         return S_FALSE;
963     }
964   }
965 
966   if (size > _curRem)
967     size = (UInt32)_curRem;
968   HRESULT res = S_OK;
969   if (_sparseMode)
970     memset(data, 0, size);
971   else
972   {
973     res = Stream->Read(data, size, &size);
974     _physPos += size;
975   }
976   if (processedSize)
977     *processedSize = size;
978   _virtPos += size;
979   _curRem -= size;
980   return res;
981 }
982 
983 Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
984 {
985   switch (seekOrigin)
986   {
987     case STREAM_SEEK_SET: break;
988     case STREAM_SEEK_CUR: offset += _virtPos; break;
989     case STREAM_SEEK_END: offset += Size; break;
990     default: return STG_E_INVALIDFUNCTION;
991   }
992   if (offset < 0)
993     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
994   if (_virtPos != (UInt64)offset)
995   {
996     _curRem = 0;
997     _virtPos = (UInt64)offset;
998   }
999   if (newPosition)
1000     *newPosition = (UInt64)offset;
1001   return S_OK;
1002 }
1003 
1004 static HRESULT DataParseExtents(unsigned clusterSizeLog, const CObjectVector<CAttr> &attrs,
1005     unsigned attrIndex, unsigned attrIndexLim, UInt64 numPhysClusters, CRecordVector<CExtent> &Extents)
1006 {
1007   {
1008     CExtent e;
1009     e.Virt = 0;
1010     e.Phy = kEmptyExtent;
1011     Extents.Add(e);
1012   }
1013 
1014   const CAttr &attr0 = attrs[attrIndex];
1015 
1016   /*
1017   if (attrs[attrIndexLim - 1].HighVcn + 1 != (attr0.AllocatedSize >> clusterSizeLog))
1018   {
1019   }
1020   */
1021 
1022   if (attr0.AllocatedSize < attr0.Size ||
1023       (attrs[attrIndexLim - 1].HighVcn + 1) != (attr0.AllocatedSize >> clusterSizeLog) ||
1024       (attr0.AllocatedSize & ((1 << clusterSizeLog) - 1)) != 0)
1025     return S_FALSE;
1026 
1027   for (unsigned i = attrIndex; i < attrIndexLim; i++)
1028     if (!attrs[i].ParseExtents(Extents, numPhysClusters, attr0.CompressionUnit))
1029       return S_FALSE;
1030 
1031   UInt64 packSizeCalc = 0;
1032   FOR_VECTOR (k, Extents)
1033   {
1034     CExtent &e = Extents[k];
1035     if (!e.IsEmpty())
1036       packSizeCalc += (Extents[k + 1].Virt - e.Virt) << clusterSizeLog;
1037     PRF2(printf("\nSize = %4I64X", Extents[k + 1].Virt - e.Virt));
1038     PRF2(printf("  Pos = %4I64X", e.Phy));
1039   }
1040 
1041   if (attr0.CompressionUnit != 0)
1042   {
1043     if (packSizeCalc != attr0.PackSize)
1044       return S_FALSE;
1045   }
1046   else
1047   {
1048     if (packSizeCalc != attr0.AllocatedSize)
1049       return S_FALSE;
1050   }
1051   return S_OK;
1052 }
1053 
1054 struct CDataRef
1055 {
1056   unsigned Start;
1057   unsigned Num;
1058 };
1059 
1060 static const UInt32 kMagic_FILE = 0x454C4946;
1061 static const UInt32 kMagic_BAAD = 0x44414142;
1062 
1063 // 22.02: we support some rare case magic values:
1064 static const UInt32 kMagic_INDX = 0x58444e49;
1065 static const UInt32 kMagic_HOLE = 0x454c4f48;
1066 static const UInt32 kMagic_RSTR = 0x52545352;
1067 static const UInt32 kMagic_RCRD = 0x44524352;
1068 static const UInt32 kMagic_CHKD = 0x444b4843;
1069 static const UInt32 kMagic_FFFFFFFF = 0xFFFFFFFF;
1070 
1071 
1072 struct CMftRec
1073 {
1074   UInt32 Magic;
1075   // UInt64 Lsn;
1076   UInt16 SeqNumber;  // Number of times this mft record has been reused
1077   UInt16 Flags;
1078   // UInt16 LinkCount;
1079   // UInt16 NextAttrInstance;
1080   CMftRef BaseMftRef;
1081   // UInt32 ThisRecNumber;
1082 
1083   UInt32 MyNumNameLinks;
1084   int MyItemIndex; // index in Items[] of main item  for that record, or -1 if there is no item for that record
1085 
1086   CObjectVector<CAttr> DataAttrs;
1087   CObjectVector<CFileNameAttr> FileNames;
1088   CRecordVector<CDataRef> DataRefs;
1089   // CAttr SecurityAttr;
1090 
1091   CSiAttr SiAttr;
1092 
1093   CByteBuffer ReparseData;
1094 
1095   int FindWin32Name_for_DosName(unsigned dosNameIndex) const
1096   {
1097     const CFileNameAttr &cur = FileNames[dosNameIndex];
1098     if (cur.IsDos())
1099       for (unsigned i = 0; i < FileNames.Size(); i++)
1100       {
1101         const CFileNameAttr &next = FileNames[i];
1102         if (next.IsWin32() && cur.ParentDirRef.Val == next.ParentDirRef.Val)
1103           return (int)i;
1104       }
1105     return -1;
1106   }
1107 
1108   int FindDosName(unsigned nameIndex) const
1109   {
1110     const CFileNameAttr &cur = FileNames[nameIndex];
1111     if (cur.IsWin32())
1112       for (unsigned i = 0; i < FileNames.Size(); i++)
1113       {
1114         const CFileNameAttr &next = FileNames[i];
1115         if (next.IsDos() && cur.ParentDirRef.Val == next.ParentDirRef.Val)
1116           return (int)i;
1117       }
1118     return -1;
1119   }
1120 
1121   /*
1122   bool IsAltStream(int dataIndex) const
1123   {
1124     return dataIndex >= 0 && (
1125       (IsDir() ||
1126       !DataAttrs[DataRefs[dataIndex].Start].Name.IsEmpty()));
1127   }
1128   */
1129 
1130   void MoveAttrsFrom(CMftRec &src)
1131   {
1132     DataAttrs += src.DataAttrs;
1133     FileNames += src.FileNames;
1134     src.DataAttrs.ClearAndFree();
1135     src.FileNames.ClearAndFree();
1136   }
1137 
1138   UInt64 GetPackSize() const
1139   {
1140     UInt64 res = 0;
1141     FOR_VECTOR (i, DataRefs)
1142       res += DataAttrs[DataRefs[i].Start].GetPackSize();
1143     return res;
1144   }
1145 
1146   bool Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 recNumber, CObjectVector<CAttr> *attrs);
1147 
1148   bool Is_Magic_Empty() const
1149   {
1150     // what exact Magic values are possible for empty and unused records?
1151     const UInt32 k_Magic_Unused_MAX = 5; // 22.02
1152     return (Magic <= k_Magic_Unused_MAX);
1153   }
1154   bool Is_Magic_FILE() const { return (Magic == kMagic_FILE); }
1155   // bool Is_Magic_BAAD() const { return (Magic == kMagic_BAAD); }
1156   bool Is_Magic_CanIgnore() const
1157   {
1158     return Is_Magic_Empty()
1159         || Magic == kMagic_BAAD
1160         || Magic == kMagic_INDX
1161         || Magic == kMagic_HOLE
1162         || Magic == kMagic_RSTR
1163         || Magic == kMagic_RCRD
1164         || Magic == kMagic_CHKD
1165         || Magic == kMagic_FFFFFFFF;
1166   }
1167 
1168   bool InUse() const { return (Flags & 1) != 0; }
1169   bool IsDir() const { return (Flags & 2) != 0; }
1170 
1171   void ParseDataNames();
1172   HRESULT GetStream(IInStream *mainStream, int dataIndex,
1173       unsigned clusterSizeLog, UInt64 numPhysClusters, IInStream **stream) const;
1174   unsigned GetNumExtents(int dataIndex, unsigned clusterSizeLog, UInt64 numPhysClusters) const;
1175 
1176   UInt64 GetSize(unsigned dataIndex) const { return DataAttrs[DataRefs[dataIndex].Start].GetSize(); }
1177 
1178   CMftRec():
1179       SeqNumber(0),
1180       Flags(0),
1181       MyNumNameLinks(0),
1182       MyItemIndex(-1) {}
1183 };
1184 
1185 void CMftRec::ParseDataNames()
1186 {
1187   DataRefs.Clear();
1188   DataAttrs.Sort(CompareAttr, NULL);
1189 
1190   for (unsigned i = 0; i < DataAttrs.Size();)
1191   {
1192     CDataRef ref;
1193     ref.Start = i;
1194     for (i++; i < DataAttrs.Size(); i++)
1195       if (DataAttrs[ref.Start].Name != DataAttrs[i].Name)
1196         break;
1197     ref.Num = i - ref.Start;
1198     DataRefs.Add(ref);
1199   }
1200 }
1201 
1202 HRESULT CMftRec::GetStream(IInStream *mainStream, int dataIndex,
1203     unsigned clusterSizeLog, UInt64 numPhysClusters, IInStream **destStream) const
1204 {
1205   *destStream = NULL;
1206   CBufferInStream *streamSpec = new CBufferInStream;
1207   CMyComPtr<IInStream> streamTemp = streamSpec;
1208 
1209   if (dataIndex >= 0)
1210   if ((unsigned)dataIndex < DataRefs.Size())
1211   {
1212     const CDataRef &ref = DataRefs[dataIndex];
1213     unsigned numNonResident = 0;
1214     unsigned i;
1215     for (i = ref.Start; i < ref.Start + ref.Num; i++)
1216       if (DataAttrs[i].NonResident)
1217         numNonResident++;
1218 
1219     const CAttr &attr0 = DataAttrs[ref.Start];
1220 
1221     if (numNonResident != 0 || ref.Num != 1)
1222     {
1223       if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported())
1224         return S_FALSE;
1225       CInStream *ss = new CInStream;
1226       CMyComPtr<IInStream> streamTemp2 = ss;
1227       RINOK(DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, ss->Extents))
1228       ss->Size = attr0.Size;
1229       ss->InitializedSize = attr0.InitializedSize;
1230       ss->Stream = mainStream;
1231       ss->BlockSizeLog = clusterSizeLog;
1232       ss->InUse = InUse();
1233       RINOK(ss->InitAndSeek(attr0.CompressionUnit))
1234       *destStream = streamTemp2.Detach();
1235       return S_OK;
1236     }
1237 
1238     streamSpec->Buf = attr0.Data;
1239   }
1240 
1241   streamSpec->Init();
1242   *destStream = streamTemp.Detach();
1243   return S_OK;
1244 }
1245 
1246 unsigned CMftRec::GetNumExtents(int dataIndex, unsigned clusterSizeLog, UInt64 numPhysClusters) const
1247 {
1248   if (dataIndex < 0)
1249     return 0;
1250   {
1251     const CDataRef &ref = DataRefs[dataIndex];
1252     unsigned numNonResident = 0;
1253     unsigned i;
1254     for (i = ref.Start; i < ref.Start + ref.Num; i++)
1255       if (DataAttrs[i].NonResident)
1256         numNonResident++;
1257 
1258     const CAttr &attr0 = DataAttrs[ref.Start];
1259 
1260     if (numNonResident != 0 || ref.Num != 1)
1261     {
1262       if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported())
1263         return 0; // error;
1264       CRecordVector<CExtent> extents;
1265       if (DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, extents) != S_OK)
1266         return 0; // error;
1267       return extents.Size() - 1;
1268     }
1269     // if (attr0.Data.Size() != 0)
1270     //   return 1;
1271     return 0;
1272   }
1273 }
1274 
1275 bool CMftRec::Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 recNumber,
1276     CObjectVector<CAttr> *attrs)
1277 {
1278   G32(p, Magic);
1279   if (!Is_Magic_FILE())
1280     return Is_Magic_CanIgnore();
1281 
1282   {
1283     UInt32 usaOffset;
1284     UInt32 numUsaItems;
1285     G16(p + 0x04, usaOffset);
1286     G16(p + 0x06, numUsaItems);
1287 
1288     /* NTFS stores (usn) to 2 last bytes in each sector (before writing record to disk).
1289        Original values of these two bytes are stored in table.
1290        So we restore original data from table */
1291 
1292     if ((usaOffset & 1) != 0
1293         || usaOffset + numUsaItems * 2 > ((UInt32)1 << sectorSizeLog) - 2
1294         || numUsaItems == 0
1295         || numUsaItems - 1 != numSectors)
1296       return false;
1297 
1298     if (usaOffset >= 0x30) // NTFS 3.1+
1299     {
1300       UInt32 iii = Get32(p + 0x2C);
1301       if (iii != recNumber)
1302       {
1303         // ntfs-3g probably writes 0 (that probably is incorrect value) to this field for unused records.
1304         // so we support that "bad" case.
1305         if (iii != 0)
1306           return false;
1307       }
1308     }
1309 
1310     UInt16 usn = Get16(p + usaOffset);
1311     // PRF(printf("\nusn = %d", usn));
1312     for (UInt32 i = 1; i < numUsaItems; i++)
1313     {
1314       void *pp = p + (i << sectorSizeLog) - 2;
1315       if (Get16(pp) != usn)
1316         return false;
1317       SetUi16(pp, Get16(p + usaOffset + i * 2))
1318     }
1319   }
1320 
1321   // G64(p + 0x08, Lsn);
1322   G16(p + 0x10, SeqNumber);
1323   // G16(p + 0x12, LinkCount);
1324   // PRF(printf(" L=%d", LinkCount));
1325   const UInt32 attrOffs = Get16(p + 0x14);
1326   G16(p + 0x16, Flags);
1327   PRF(printf(" F=%4X", Flags));
1328 
1329   const UInt32 bytesInUse = Get32(p + 0x18);
1330   const UInt32 bytesAlloc = Get32(p + 0x1C);
1331   G64(p + 0x20, BaseMftRef.Val);
1332   if (BaseMftRef.Val != 0)
1333   {
1334     PRF(printf("  BaseRef=%d", (int)BaseMftRef.Val));
1335     // return false; // Check it;
1336   }
1337   // G16(p + 0x28, NextAttrInstance);
1338 
1339   UInt32 limit = numSectors << sectorSizeLog;
1340   if (attrOffs >= limit
1341       || (attrOffs & 7) != 0
1342       || (bytesInUse & 7) != 0
1343       || bytesInUse > limit
1344       || bytesAlloc != limit)
1345     return false;
1346 
1347   limit = bytesInUse;
1348 
1349   for (UInt32 t = attrOffs;;)
1350   {
1351     if (t >= limit)
1352       return false;
1353 
1354     CAttr attr;
1355     // PRF(printf("\n  %2d:", Attrs.Size()));
1356     PRF(printf("\n"));
1357     UInt32 len = attr.Parse(p + t, limit - t);
1358     if (len == 0 || limit - t < len)
1359       return false;
1360     t += len;
1361     if (attr.Type == 0xFFFFFFFF)
1362     {
1363       if (t != limit)
1364         return false;
1365       break;
1366     }
1367     switch (attr.Type)
1368     {
1369       case ATTR_TYPE_FILE_NAME:
1370       {
1371         CFileNameAttr fna;
1372         if (!attr.ParseFileName(fna))
1373           return false;
1374         FileNames.Add(fna);
1375         PRF(printf("  flags = %4x\n  ", (int)fna.NameType));
1376         PRF_UTF16(fna.Name);
1377         break;
1378       }
1379       case ATTR_TYPE_STANDARD_INFO:
1380         if (!attr.ParseSi(SiAttr))
1381           return false;
1382         break;
1383       case ATTR_TYPE_DATA:
1384         DataAttrs.Add(attr);
1385         break;
1386       case ATTR_TYPE_REPARSE_POINT:
1387         ReparseData = attr.Data;
1388         break;
1389       /*
1390       case ATTR_TYPE_SECURITY_DESCRIPTOR:
1391         SecurityAttr = attr;
1392         break;
1393       */
1394       default:
1395         if (attrs)
1396           attrs->Add(attr);
1397         break;
1398     }
1399   }
1400 
1401   return true;
1402 }
1403 
1404 /*
1405   NTFS probably creates empty DATA_ATTRIBUTE for empty file,
1406   But it doesn't do it for
1407     $Secure (:$SDS),
1408     $Extend\$Quota
1409     $Extend\$ObjId
1410     $Extend\$Reparse
1411 */
1412 
1413 static const int k_Item_DataIndex_IsEmptyFile = -1; // file without unnamed data stream
1414 static const int k_Item_DataIndex_IsDir = -2;
1415 
1416 // static const int k_ParentFolderIndex_Root = -1;
1417 static const int k_ParentFolderIndex_Lost = -2;
1418 static const int k_ParentFolderIndex_Deleted = -3;
1419 
1420 struct CItem
1421 {
1422   unsigned RecIndex;  // index in Recs array
1423   unsigned NameIndex; // index in CMftRec::FileNames
1424 
1425   int DataIndex;      /* index in CMftRec::DataRefs
1426                          -1: file without unnamed data stream
1427                          -2: for directories */
1428 
1429   int ParentFolder;   /* index in Items array
1430                          -1: for root items
1431                          -2: [LOST] folder
1432                          -3: [UNKNOWN] folder (deleted lost) */
1433   int ParentHost;     /* index in Items array, if it's AltStream
1434                          -1: if it's not AltStream */
1435 
1436   CItem(): DataIndex(k_Item_DataIndex_IsDir), ParentFolder(-1), ParentHost(-1) {}
1437 
1438   bool IsAltStream() const { return ParentHost != -1; }
1439   bool IsDir() const { return DataIndex == k_Item_DataIndex_IsDir; }
1440         // check it !!!
1441         // probably NTFS for empty file still creates empty DATA_ATTRIBUTE
1442         // But it doesn't do it for $Secure:$SDS
1443 };
1444 
1445 struct CDatabase
1446 {
1447   CRecordVector<CItem> Items;
1448   CObjectVector<CMftRec> Recs;
1449   CMyComPtr<IInStream> InStream;
1450   CHeader Header;
1451   UInt64 PhySize;
1452 
1453   IArchiveOpenCallback *OpenCallback;
1454 
1455   CByteBuffer ByteBuf;
1456 
1457   CObjectVector<CAttr> VolAttrs;
1458 
1459   CByteBuffer SecurData;
1460   CRecordVector<size_t> SecurOffsets;
1461 
1462   // bool _headerWarning;
1463   bool ThereAreAltStreams;
1464 
1465   bool _showSystemFiles;
1466   bool _showDeletedFiles;
1467   CObjectVector<UString2> VirtFolderNames;
1468   UString EmptyString;
1469 
1470   int _systemFolderIndex;
1471   int _lostFolderIndex_Normal;
1472   int _lostFolderIndex_Deleted;
1473 
1474   void InitProps()
1475   {
1476     _showSystemFiles = true;
1477     // we show SystemFiles by default since it's difficult to track $Extend\* system files
1478     // it must be fixed later
1479     _showDeletedFiles = false;
1480   }
1481 
1482   CDatabase() { InitProps(); }
1483   ~CDatabase() { ClearAndClose(); }
1484 
1485   void Clear();
1486   void ClearAndClose();
1487 
1488   void GetItemPath(unsigned index, NCOM::CPropVariant &path) const;
1489   HRESULT Open();
1490 
1491   HRESULT SeekToCluster(UInt64 cluster);
1492 
1493   int Find_DirItem_For_MftRec(UInt64 recIndex) const
1494   {
1495     if (recIndex >= Recs.Size())
1496       return -1;
1497     const CMftRec &rec = Recs[(unsigned)recIndex];
1498     if (!rec.IsDir())
1499       return -1;
1500     return rec.MyItemIndex;
1501     /*
1502     unsigned left = 0, right = Items.Size();
1503     while (left != right)
1504     {
1505       unsigned mid = (left + right) / 2;
1506       const CItem &item = Items[mid];
1507       UInt64 midValue = item.RecIndex;
1508       if (recIndex == midValue)
1509       {
1510         // if item is not dir (file or alt stream we don't return it)
1511         // if (item.DataIndex < 0)
1512         if (item.IsDir())
1513           return mid;
1514         right = mid;
1515       }
1516       else if (recIndex < midValue)
1517         right = mid;
1518       else
1519         left = mid + 1;
1520     }
1521     return -1;
1522     */
1523   }
1524 
1525   bool FindSecurityDescritor(UInt32 id, UInt64 &offset, UInt32 &size) const;
1526 
1527   HRESULT ParseSecuritySDS_2();
1528   void ParseSecuritySDS()
1529   {
1530     HRESULT res = ParseSecuritySDS_2();
1531     if (res != S_OK)
1532     {
1533       SecurOffsets.Clear();
1534       SecurData.Free();
1535     }
1536   }
1537 
1538 };
1539 
1540 HRESULT CDatabase::SeekToCluster(UInt64 cluster)
1541 {
1542   return InStream_SeekSet(InStream, cluster << Header.ClusterSizeLog);
1543 }
1544 
1545 void CDatabase::Clear()
1546 {
1547   Items.Clear();
1548   Recs.Clear();
1549   SecurOffsets.Clear();
1550   SecurData.Free();
1551   VirtFolderNames.Clear();
1552   _systemFolderIndex = -1;
1553   _lostFolderIndex_Normal = -1;
1554   _lostFolderIndex_Deleted = -1;
1555   ThereAreAltStreams = false;
1556   // _headerWarning = false;
1557   PhySize = 0;
1558 }
1559 
1560 void CDatabase::ClearAndClose()
1561 {
1562   Clear();
1563   InStream.Release();
1564 }
1565 
1566 
1567 static void CopyName(wchar_t *dest, const wchar_t *src)
1568 {
1569   for (;;)
1570   {
1571     wchar_t c = *src++;
1572     // 18.06
1573     if (c == '\\' || c == '/')
1574       c = '_';
1575     *dest++ = c;
1576     if (c == 0)
1577       return;
1578   }
1579 }
1580 
1581 void CDatabase::GetItemPath(unsigned index, NCOM::CPropVariant &path) const
1582 {
1583   const CItem *item = &Items[index];
1584   unsigned size = 0;
1585   const CMftRec &rec = Recs[item->RecIndex];
1586   size += rec.FileNames[item->NameIndex].Name.Len();
1587 
1588   bool isAltStream = item->IsAltStream();
1589 
1590   if (isAltStream)
1591   {
1592     const CAttr &data = rec.DataAttrs[rec.DataRefs[item->DataIndex].Start];
1593     if (item->RecIndex == kRecIndex_RootDir)
1594     {
1595       wchar_t *s = path.AllocBstr(data.Name.Len() + 1);
1596       s[0] = L':';
1597       if (!data.Name.IsEmpty())
1598         CopyName(s + 1, data.Name.GetRawPtr());
1599       return;
1600     }
1601 
1602     size += data.Name.Len();
1603     size++;
1604   }
1605 
1606   for (unsigned i = 0;; i++)
1607   {
1608     if (i > 256)
1609     {
1610       path = "[TOO-LONG]";
1611       return;
1612     }
1613     const wchar_t *servName;
1614     if (item->RecIndex < kNumSysRecs
1615         /* && item->RecIndex != kRecIndex_RootDir */)
1616       servName = kVirtualFolder_System;
1617     else
1618     {
1619       int index2 = item->ParentFolder;
1620       if (index2 >= 0)
1621       {
1622         item = &Items[index2];
1623         size += Recs[item->RecIndex].FileNames[item->NameIndex].Name.Len() + 1;
1624         continue;
1625       }
1626       if (index2 == -1)
1627         break;
1628       servName = (index2 == k_ParentFolderIndex_Lost) ?
1629           kVirtualFolder_Lost_Normal :
1630           kVirtualFolder_Lost_Deleted;
1631     }
1632     size += MyStringLen(servName) + 1;
1633     break;
1634   }
1635 
1636   wchar_t *s = path.AllocBstr(size);
1637 
1638   item = &Items[index];
1639 
1640   bool needColon = false;
1641   if (isAltStream)
1642   {
1643     const UString2 &name = rec.DataAttrs[rec.DataRefs[item->DataIndex].Start].Name;
1644     if (!name.IsEmpty())
1645     {
1646       size -= name.Len();
1647       CopyName(s + size, name.GetRawPtr());
1648     }
1649     s[--size] = ':';
1650     needColon = true;
1651   }
1652 
1653   {
1654     const UString2 &name = rec.FileNames[item->NameIndex].Name;
1655     unsigned len = name.Len();
1656     if (len != 0)
1657       CopyName(s + size - len, name.GetRawPtr());
1658     if (needColon)
1659       s[size] =  ':';
1660     size -= len;
1661   }
1662 
1663   for (;;)
1664   {
1665     const wchar_t *servName;
1666     if (item->RecIndex < kNumSysRecs
1667         /* && && item->RecIndex != kRecIndex_RootDir */)
1668       servName = kVirtualFolder_System;
1669     else
1670     {
1671       int index2 = item->ParentFolder;
1672       if (index2 >= 0)
1673       {
1674         item = &Items[index2];
1675         const UString2 &name = Recs[item->RecIndex].FileNames[item->NameIndex].Name;
1676         unsigned len = name.Len();
1677         size--;
1678         if (len != 0)
1679         {
1680           size -= len;
1681           CopyName(s + size, name.GetRawPtr());
1682         }
1683         s[size + len] = WCHAR_PATH_SEPARATOR;
1684         continue;
1685       }
1686       if (index2 == -1)
1687         break;
1688       servName = (index2 == k_ParentFolderIndex_Lost) ?
1689           kVirtualFolder_Lost_Normal :
1690           kVirtualFolder_Lost_Deleted;
1691     }
1692     MyStringCopy(s, servName);
1693     s[MyStringLen(servName)] = WCHAR_PATH_SEPARATOR;
1694     break;
1695   }
1696 }
1697 
1698 bool CDatabase::FindSecurityDescritor(UInt32 item, UInt64 &offset, UInt32 &size) const
1699 {
1700   offset = 0;
1701   size = 0;
1702   unsigned left = 0, right = SecurOffsets.Size();
1703   while (left != right)
1704   {
1705     unsigned mid = (left + right) / 2;
1706     size_t offs = SecurOffsets[mid];
1707     UInt32 midValue = Get32(((const Byte *)SecurData) + offs + 4);
1708     if (item == midValue)
1709     {
1710       offset = Get64((const Byte *)SecurData + offs + 8) + 20;
1711       size = Get32((const Byte *)SecurData + offs + 16) - 20;
1712       return true;
1713     }
1714     if (item < midValue)
1715       right = mid;
1716     else
1717       left = mid + 1;
1718   }
1719   return false;
1720 }
1721 
1722 /*
1723 static int CompareIDs(const size_t *p1, const size_t *p2, void *data)
1724 {
1725   UInt32 id1 = Get32(((const Byte *)data) + *p1 + 4);
1726   UInt32 id2 = Get32(((const Byte *)data) + *p2 + 4);
1727   return MyCompare(id1, id2);
1728 }
1729 */
1730 
1731 // security data contains duplication copy after each 256 KB.
1732 static const unsigned kSecureDuplicateStepBits = 18;
1733 
1734 HRESULT CDatabase::ParseSecuritySDS_2()
1735 {
1736   const Byte *p = SecurData;
1737   size_t size = SecurData.Size();
1738   const size_t kDuplicateStep = (size_t)1 << kSecureDuplicateStepBits;
1739   const size_t kDuplicateMask = kDuplicateStep - 1;
1740   size_t lim = MyMin(size, kDuplicateStep);
1741   UInt32 idPrev = 0;
1742   for (size_t pos = 0; pos < size && size - pos >= 20;)
1743   {
1744     UInt32 id = Get32(p + pos + 4);
1745     UInt64 offs = Get64(p + pos + 8);
1746     UInt32 entrySize = Get32(p + pos + 16);
1747     if (offs == pos && entrySize >= 20 && lim - pos >= entrySize)
1748     {
1749       if (id <= idPrev)
1750         return S_FALSE;
1751       idPrev = id;
1752       SecurOffsets.Add(pos);
1753       pos += entrySize;
1754       pos = (pos + 0xF) & ~(size_t)0xF;
1755       if ((pos & kDuplicateMask) != 0)
1756         continue;
1757     }
1758     else
1759       pos = (pos + kDuplicateStep) & ~kDuplicateMask;
1760     pos += kDuplicateStep;
1761     lim = pos + kDuplicateStep;
1762     if (lim >= size)
1763       lim = size;
1764   }
1765   // we checked that IDs are sorted, so we don't need Sort
1766   // SecurOffsets.Sort(CompareIDs, (void *)p);
1767   return S_OK;
1768 }
1769 
1770 HRESULT CDatabase::Open()
1771 {
1772   Clear();
1773 
1774   /* NTFS layout:
1775      1) main part (as specified by NumClusters). Only that part is available, if we open "\\.\c:"
1776      2) additional empty sectors (as specified by NumSectors)
1777      3) the copy of first sector (boot sector)
1778 
1779      We support both cases:
1780       - the file with only main part
1781       - full file (as raw data on partition), including the copy
1782         of first sector (boot sector) at the end of data
1783 
1784      We don't support the case, when only the copy of boot sector
1785      at the end was detected as NTFS signature.
1786   */
1787 
1788   {
1789     const UInt32 kHeaderSize = 512;
1790     Byte buf[kHeaderSize];
1791     RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize))
1792     if (!Header.Parse(buf))
1793       return S_FALSE;
1794 
1795     UInt64 fileSize;
1796     RINOK(InStream_GetSize_SeekToEnd(InStream, fileSize))
1797     PhySize = Header.GetPhySize_Clusters();
1798     if (fileSize < PhySize)
1799       return S_FALSE;
1800 
1801     UInt64 phySizeMax = Header.GetPhySize_Max();
1802     if (fileSize >= phySizeMax)
1803     {
1804       RINOK(InStream_SeekSet(InStream, Header.NumSectors << Header.SectorSizeLog))
1805       Byte buf2[kHeaderSize];
1806       if (ReadStream_FALSE(InStream, buf2, kHeaderSize) == S_OK)
1807       {
1808         if (memcmp(buf, buf2, kHeaderSize) == 0)
1809           PhySize = phySizeMax;
1810         // else _headerWarning = true;
1811       }
1812     }
1813   }
1814 
1815   SeekToCluster(Header.MftCluster);
1816 
1817   // we use ByteBuf for records reading.
1818   // so the size of ByteBuf must be >= mftRecordSize
1819   const size_t recSize = (size_t)1 << Header.MftRecordSizeLog;
1820   const size_t kBufSize = MyMax((size_t)(1 << 15), recSize);
1821   ByteBuf.Alloc(kBufSize);
1822   RINOK(ReadStream_FALSE(InStream, ByteBuf, recSize))
1823   {
1824     const UInt32 allocSize = Get32(ByteBuf + 0x1C);
1825     if (allocSize != recSize)
1826       return S_FALSE;
1827   }
1828   // MftRecordSizeLog >= SectorSizeLog
1829   const UInt32 numSectorsInRec = 1u << (Header.MftRecordSizeLog - Header.SectorSizeLog);
1830   CMyComPtr<IInStream> mftStream;
1831   CMftRec mftRec;
1832   {
1833     if (!mftRec.Parse(ByteBuf, Header.SectorSizeLog, numSectorsInRec, 0, NULL))
1834       return S_FALSE;
1835     if (!mftRec.Is_Magic_FILE())
1836       return S_FALSE;
1837     mftRec.ParseDataNames();
1838     if (mftRec.DataRefs.IsEmpty())
1839       return S_FALSE;
1840     RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream))
1841     if (!mftStream)
1842       return S_FALSE;
1843   }
1844 
1845   // CObjectVector<CAttr> SecurityAttrs;
1846 
1847   const UInt64 mftSize = mftRec.DataAttrs[0].Size;
1848   if ((mftSize >> 4) > Header.GetPhySize_Clusters())
1849     return S_FALSE;
1850 
1851   {
1852     const UInt64 numFiles = mftSize >> Header.MftRecordSizeLog;
1853     if (numFiles > (1 << 30))
1854       return S_FALSE;
1855     if (OpenCallback)
1856     {
1857       RINOK(OpenCallback->SetTotal(&numFiles, &mftSize))
1858     }
1859     Recs.ClearAndReserve((unsigned)numFiles);
1860   }
1861 
1862   for (UInt64 pos64 = 0;;)
1863   {
1864     if (OpenCallback)
1865     {
1866       const UInt64 numFiles = Recs.Size();
1867       if ((numFiles & 0x3FFF) == 0)
1868       {
1869         RINOK(OpenCallback->SetCompleted(&numFiles, &pos64))
1870       }
1871     }
1872     size_t readSize = kBufSize;
1873     {
1874       const UInt64 rem = mftSize - pos64;
1875       if (readSize > rem)
1876         readSize = (size_t)rem;
1877     }
1878     if (readSize < recSize)
1879       break;
1880     RINOK(ReadStream_FALSE(mftStream, ByteBuf, readSize))
1881     pos64 += readSize;
1882 
1883     for (size_t i = 0; readSize >= recSize; i += recSize, readSize -= recSize)
1884     {
1885       PRF(printf("\n---------------------"));
1886       PRF(printf("\n%5d:", Recs.Size()));
1887 
1888       Byte *p = ByteBuf + i;
1889       CMftRec rec;
1890 
1891       CObjectVector<CAttr> *attrs = NULL;
1892       unsigned recIndex = Recs.Size();
1893       switch (recIndex)
1894       {
1895         case kRecIndex_Volume: attrs = &VolAttrs; break;
1896         // case kRecIndex_Security: attrs = &SecurityAttrs; break;
1897       }
1898 
1899       if (!rec.Parse(p, Header.SectorSizeLog, numSectorsInRec, (UInt32)Recs.Size(), attrs))
1900         return S_FALSE;
1901       Recs.Add(rec);
1902     }
1903   }
1904 
1905   /*
1906   // that code looks too complex. And we can get security info without index parsing
1907   for (i = 0; i < SecurityAttrs.Size(); i++)
1908   {
1909     const CAttr &attr = SecurityAttrs[i];
1910     if (attr.Name == L"$SII")
1911     {
1912       if (attr.Type == ATTR_TYPE_INDEX_ROOT)
1913       {
1914         const Byte *data = attr.Data;
1915         size_t size = attr.Data.Size();
1916 
1917         // Index Root
1918         UInt32 attrType = Get32(data);
1919         UInt32 collationRule = Get32(data + 4);
1920         UInt32 indexAllocationEtrySizeSize = Get32(data + 8);
1921         UInt32 clustersPerIndexRecord = Get32(data + 0xC);
1922         data += 0x10;
1923 
1924         // Index Header
1925         UInt32 firstEntryOffset = Get32(data);
1926         UInt32 totalSize = Get32(data + 4);
1927         UInt32 allocSize = Get32(data + 8);
1928         UInt32 flags = Get32(data + 0xC);
1929 
1930         int num = 0;
1931         for (int j = 0 ; j < num; j++)
1932         {
1933           if (Get32(data) != 0x1414 || // offset and size
1934               Get32(data + 4) != 0 ||
1935               Get32(data + 8) != 0x428) // KeySize / EntrySize
1936             break;
1937           UInt32 flags = Get32(data + 12);
1938           UInt32 id = Get32(data + 0x10);
1939           if (id = Get32(data + 0x18))
1940             break;
1941           UInt32 descriptorOffset = Get64(data + 0x1C);
1942           UInt32 descriptorSize = Get64(data + 0x24);
1943           data += 0x28;
1944         }
1945         // break;
1946       }
1947     }
1948   }
1949   */
1950 
1951   unsigned i;
1952 
1953   for (i = 0; i < Recs.Size(); i++)
1954   {
1955     CMftRec &rec = Recs[i];
1956     if (!rec.Is_Magic_FILE())
1957       continue;
1958 
1959     if (!rec.BaseMftRef.IsBaseItself())
1960     {
1961       const UInt64 refIndex = rec.BaseMftRef.GetIndex();
1962       if (refIndex >= Recs.Size())
1963         return S_FALSE;
1964       CMftRec &refRec = Recs[(unsigned)refIndex];
1965       if (!refRec.Is_Magic_FILE())
1966         continue;
1967 
1968       bool moveAttrs = (refRec.SeqNumber == rec.BaseMftRef.GetNumber() && refRec.BaseMftRef.IsBaseItself());
1969       if (rec.InUse() && refRec.InUse())
1970       {
1971         if (!moveAttrs)
1972           return S_FALSE;
1973       }
1974       else if (rec.InUse() || refRec.InUse())
1975         moveAttrs = false;
1976       if (moveAttrs)
1977         refRec.MoveAttrsFrom(rec);
1978     }
1979   }
1980 
1981   for (i = 0; i < Recs.Size(); i++)
1982   {
1983     CMftRec &rec = Recs[i];
1984     if (!rec.Is_Magic_FILE())
1985       continue;
1986     rec.ParseDataNames();
1987   }
1988 
1989   for (i = 0; i < Recs.Size(); i++)
1990   {
1991     CMftRec &rec = Recs[i];
1992     if (!rec.Is_Magic_FILE() || !rec.BaseMftRef.IsBaseItself())
1993       continue;
1994     if (i < kNumSysRecs && !_showSystemFiles)
1995       continue;
1996     if (!rec.InUse() && !_showDeletedFiles)
1997       continue;
1998 
1999     rec.MyNumNameLinks = rec.FileNames.Size();
2000 
2001     // printf("\n%4d: ", i);
2002 
2003     /* Actually DataAttrs / DataRefs are sorted by name.
2004        It can not be more than one unnamed stream in DataRefs
2005        And indexOfUnnamedStream <= 0.
2006     */
2007 
2008     int indexOfUnnamedStream = -1;
2009     if (!rec.IsDir())
2010     {
2011       FOR_VECTOR (di, rec.DataRefs)
2012         if (rec.DataAttrs[rec.DataRefs[di].Start].Name.IsEmpty())
2013         {
2014           indexOfUnnamedStream = (int)di;
2015           break;
2016         }
2017     }
2018 
2019     if (rec.FileNames.IsEmpty())
2020     {
2021       bool needShow = true;
2022       if (i < kNumSysRecs)
2023       {
2024         needShow = false;
2025         FOR_VECTOR (di, rec.DataRefs)
2026           if (rec.GetSize(di) != 0)
2027           {
2028             needShow = true;
2029             break;
2030           }
2031       }
2032       if (needShow)
2033       {
2034         CFileNameAttr &fna = rec.FileNames.AddNew();
2035         // we set incorrect ParentDirRef, that will place item to [LOST] folder
2036         fna.ParentDirRef.Val = (UInt64)(Int64)-1;
2037         char s[16 + 16];
2038         ConvertUInt32ToString(i, MyStpCpy(s, "[NONAME]-"));
2039         fna.Name.SetFromAscii(s);
2040         fna.NameType = kFileNameType_Win32Dos;
2041         fna.Attrib = 0;
2042       }
2043     }
2044 
2045     // bool isMainName = true;
2046 
2047     FOR_VECTOR (t, rec.FileNames)
2048     {
2049       #ifdef SHOW_DEBUG_INFO
2050       const CFileNameAttr &fna = rec.FileNames[t];
2051       #endif
2052       PRF(printf("\n %4d ", (int)fna.NameType));
2053       PRF_UTF16(fna.Name);
2054       // PRF(printf("  | "));
2055 
2056       if (rec.FindWin32Name_for_DosName(t) >= 0)
2057       {
2058         rec.MyNumNameLinks--;
2059         continue;
2060       }
2061 
2062       CItem item;
2063       item.NameIndex = t;
2064       item.RecIndex = i;
2065       item.DataIndex = rec.IsDir() ?
2066           k_Item_DataIndex_IsDir :
2067             (indexOfUnnamedStream < 0 ?
2068           k_Item_DataIndex_IsEmptyFile :
2069           indexOfUnnamedStream);
2070 
2071       if (rec.MyItemIndex < 0)
2072         rec.MyItemIndex = (int)Items.Size();
2073       item.ParentHost = (int)Items.Add(item);
2074 
2075       /* we can use that code to reduce the number of alt streams:
2076          it will not show how alt streams for hard links. */
2077       // if (!isMainName) continue; isMainName = false;
2078 
2079       // unsigned numAltStreams = 0;
2080 
2081       FOR_VECTOR (di, rec.DataRefs)
2082       {
2083         if (!rec.IsDir() && (int)di == indexOfUnnamedStream)
2084           continue;
2085 
2086         const UString2 &subName = rec.DataAttrs[rec.DataRefs[di].Start].Name;
2087 
2088         PRF(printf("\n alt stream: "));
2089         PRF_UTF16(subName);
2090 
2091         {
2092           // $BadClus:$Bad is sparse file for all clusters. So we skip it.
2093           if (i == kRecIndex_BadClus && subName == L"$Bad")
2094             continue;
2095         }
2096 
2097         // numAltStreams++;
2098         ThereAreAltStreams = true;
2099         item.DataIndex = (int)di;
2100         Items.Add(item);
2101       }
2102     }
2103   }
2104 
2105   if (Recs.Size() > kRecIndex_Security)
2106   {
2107     const CMftRec &rec = Recs[kRecIndex_Security];
2108     FOR_VECTOR (di, rec.DataRefs)
2109     {
2110       const CAttr &attr = rec.DataAttrs[rec.DataRefs[di].Start];
2111       if (attr.Name == L"$SDS")
2112       {
2113         CMyComPtr<IInStream> sdsStream;
2114         RINOK(rec.GetStream(InStream, (int)di, Header.ClusterSizeLog, Header.NumClusters, &sdsStream))
2115         if (sdsStream)
2116         {
2117           const UInt64 size64 = attr.GetSize();
2118           if (size64 < (UInt32)1 << 29)
2119           {
2120             size_t size = (size_t)size64;
2121             if ((((size + 1) >> kSecureDuplicateStepBits) & 1) != 0)
2122             {
2123               size -= (1 << kSecureDuplicateStepBits);
2124               SecurData.Alloc(size);
2125               if (ReadStream_FALSE(sdsStream, SecurData, size) == S_OK)
2126               {
2127                 ParseSecuritySDS();
2128                 break;
2129               }
2130             }
2131           }
2132         }
2133         break;
2134       }
2135     }
2136   }
2137 
2138   bool thereAreUnknownFolders_Normal = false;
2139   bool thereAreUnknownFolders_Deleted = false;
2140 
2141   for (i = 0; i < Items.Size(); i++)
2142   {
2143     CItem &item = Items[i];
2144     const CMftRec &rec = Recs[item.RecIndex];
2145     const CFileNameAttr &fn = rec.FileNames[item.NameIndex];
2146     const CMftRef &parentDirRef = fn.ParentDirRef;
2147     const UInt64 refIndex = parentDirRef.GetIndex();
2148     if (refIndex == kRecIndex_RootDir)
2149       item.ParentFolder = -1;
2150     else
2151     {
2152       int index = Find_DirItem_For_MftRec(refIndex);
2153       if (index < 0 ||
2154           Recs[Items[index].RecIndex].SeqNumber != parentDirRef.GetNumber())
2155       {
2156         if (Recs[item.RecIndex].InUse())
2157         {
2158           thereAreUnknownFolders_Normal = true;
2159           index = k_ParentFolderIndex_Lost;
2160         }
2161         else
2162         {
2163           thereAreUnknownFolders_Deleted = true;
2164           index = k_ParentFolderIndex_Deleted;
2165         }
2166       }
2167       item.ParentFolder = index;
2168     }
2169   }
2170 
2171   unsigned virtIndex = Items.Size();
2172   if (_showSystemFiles)
2173   {
2174     _systemFolderIndex = (int)(virtIndex++);
2175     VirtFolderNames.Add(kVirtualFolder_System);
2176   }
2177   if (thereAreUnknownFolders_Normal)
2178   {
2179     _lostFolderIndex_Normal = (int)(virtIndex++);
2180     VirtFolderNames.Add(kVirtualFolder_Lost_Normal);
2181   }
2182   if (thereAreUnknownFolders_Deleted)
2183   {
2184     _lostFolderIndex_Deleted = (int)(virtIndex++);
2185     VirtFolderNames.Add(kVirtualFolder_Lost_Deleted);
2186   }
2187 
2188   return S_OK;
2189 }
2190 
2191 Z7_class_CHandler_final:
2192   public IInArchive,
2193   public IArchiveGetRawProps,
2194   public IInArchiveGetStream,
2195   public ISetProperties,
2196   public CMyUnknownImp,
2197   public CDatabase
2198 {
2199   Z7_IFACES_IMP_UNK_4(
2200       IInArchive,
2201       IArchiveGetRawProps,
2202       IInArchiveGetStream,
2203       ISetProperties)
2204 };
2205 
2206 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
2207 {
2208   *numProps = 2;
2209   return S_OK;
2210 }
2211 
2212 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID))
2213 {
2214   *name = NULL;
2215   *propID = index == 0 ? kpidNtReparse : kpidNtSecure;
2216   return S_OK;
2217 }
2218 
2219 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType))
2220 {
2221   *parentType = NParentType::kDir;
2222   int par = -1;
2223 
2224   if (index < Items.Size())
2225   {
2226     const CItem &item = Items[index];
2227 
2228     if (item.ParentHost >= 0)
2229     {
2230       *parentType = NParentType::kAltStream;
2231       par = (item.RecIndex == kRecIndex_RootDir ? -1 : item.ParentHost);
2232     }
2233     else if (item.RecIndex < kNumSysRecs)
2234     {
2235       if (_showSystemFiles)
2236         par = _systemFolderIndex;
2237     }
2238     else if (item.ParentFolder >= 0)
2239       par = item.ParentFolder;
2240     else if (item.ParentFolder == k_ParentFolderIndex_Lost)
2241       par = _lostFolderIndex_Normal;
2242     else if (item.ParentFolder == k_ParentFolderIndex_Deleted)
2243       par = _lostFolderIndex_Deleted;
2244   }
2245   *parent = (UInt32)(Int32)par;
2246   return S_OK;
2247 }
2248 
2249 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
2250 {
2251   *data = NULL;
2252   *dataSize = 0;
2253   *propType = 0;
2254 
2255   if (propID == kpidName)
2256   {
2257     #ifdef MY_CPU_LE
2258     const UString2 *s;
2259     if (index >= Items.Size())
2260       s = &VirtFolderNames[index - Items.Size()];
2261     else
2262     {
2263       const CItem &item = Items[index];
2264       const CMftRec &rec = Recs[item.RecIndex];
2265       if (item.IsAltStream())
2266         s = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start].Name;
2267       else
2268         s = &rec.FileNames[item.NameIndex].Name;
2269     }
2270     if (s->IsEmpty())
2271       *data = (const wchar_t *)EmptyString;
2272     else
2273       *data = s->GetRawPtr();
2274     *dataSize = (s->Len() + 1) * (UInt32)sizeof(wchar_t);
2275     *propType = PROP_DATA_TYPE_wchar_t_PTR_Z_LE;
2276     #endif
2277     return S_OK;
2278   }
2279 
2280   if (propID == kpidNtReparse)
2281   {
2282     if (index >= Items.Size())
2283       return S_OK;
2284     const CItem &item = Items[index];
2285     const CMftRec &rec = Recs[item.RecIndex];
2286     const CByteBuffer &reparse = rec.ReparseData;
2287 
2288     if (reparse.Size() != 0)
2289     {
2290       *dataSize = (UInt32)reparse.Size();
2291       *propType = NPropDataType::kRaw;
2292       *data = (const Byte *)reparse;
2293     }
2294   }
2295 
2296   if (propID == kpidNtSecure)
2297   {
2298     if (index >= Items.Size())
2299       return S_OK;
2300     const CItem &item = Items[index];
2301     const CMftRec &rec = Recs[item.RecIndex];
2302     if (rec.SiAttr.SecurityId > 0)
2303     {
2304       UInt64 offset;
2305       UInt32 size;
2306       if (FindSecurityDescritor(rec.SiAttr.SecurityId, offset, size))
2307       {
2308         *dataSize = size;
2309         *propType = NPropDataType::kRaw;
2310         *data = (const Byte *)SecurData + offset;
2311       }
2312     }
2313   }
2314 
2315   return S_OK;
2316 }
2317 
2318 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
2319 {
2320   COM_TRY_BEGIN
2321   *stream = NULL;
2322   if (index >= Items.Size())
2323     return S_OK;
2324   IInStream *stream2;
2325   const CItem &item = Items[index];
2326   const CMftRec &rec = Recs[item.RecIndex];
2327   HRESULT res = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &stream2);
2328   *stream = (ISequentialInStream *)stream2;
2329   return res;
2330   COM_TRY_END
2331 }
2332 
2333 /*
2334 enum
2335 {
2336   kpidLink2 = kpidUserDefined,
2337   kpidLinkType,
2338   kpidRecMTime,
2339   kpidRecMTime2,
2340   kpidMTime2,
2341   kpidCTime2,
2342   kpidATime2
2343 };
2344 
2345 static const CStatProp kProps[] =
2346 {
2347   { NULL, kpidPath, VT_BSTR},
2348   { NULL, kpidSize, VT_UI8},
2349   { NULL, kpidPackSize, VT_UI8},
2350 
2351   // { NULL, kpidLink, VT_BSTR},
2352 
2353   // { "Link 2", kpidLink2, VT_BSTR},
2354   // { "Link Type", kpidLinkType, VT_UI2},
2355   { NULL, kpidINode, VT_UI8},
2356 
2357   { NULL, kpidMTime, VT_FILETIME},
2358   { NULL, kpidCTime, VT_FILETIME},
2359   { NULL, kpidATime, VT_FILETIME},
2360 
2361   // { "Record Modified", kpidRecMTime, VT_FILETIME},
2362 
2363   // { "Modified 2", kpidMTime2, VT_FILETIME},
2364   // { "Created 2", kpidCTime2, VT_FILETIME},
2365   // { "Accessed 2", kpidATime2, VT_FILETIME},
2366   // { "Record Modified 2", kpidRecMTime2, VT_FILETIME},
2367 
2368   { NULL, kpidAttrib, VT_UI4},
2369   { NULL, kpidNumBlocks, VT_UI4},
2370   { NULL, kpidIsDeleted, VT_BOOL},
2371 };
2372 */
2373 
2374 static const Byte kProps[] =
2375 {
2376   kpidPath,
2377   kpidIsDir,
2378   kpidSize,
2379   kpidPackSize,
2380   kpidMTime,
2381   kpidCTime,
2382   kpidATime,
2383   kpidChangeTime,
2384   kpidAttrib,
2385   kpidLinks,
2386   kpidINode,
2387   kpidNumBlocks,
2388   kpidNumAltStreams,
2389   kpidIsAltStream,
2390   kpidShortName,
2391   kpidIsDeleted
2392 };
2393 
2394 enum
2395 {
2396   kpidRecordSize = kpidUserDefined
2397 };
2398 
2399 static const CStatProp kArcProps[] =
2400 {
2401   { NULL, kpidVolumeName, VT_BSTR},
2402   { NULL, kpidFileSystem, VT_BSTR},
2403   { NULL, kpidClusterSize, VT_UI4},
2404   { NULL, kpidSectorSize, VT_UI4},
2405   { "MFT Record Size", kpidRecordSize, VT_UI4},
2406   { NULL, kpidHeadersSize, VT_UI8},
2407   { NULL, kpidCTime, VT_FILETIME},
2408   { NULL, kpidId, VT_UI8},
2409 };
2410 
2411 /*
2412 static const Byte kArcProps[] =
2413 {
2414   kpidVolumeName,
2415   kpidFileSystem,
2416   kpidClusterSize,
2417   kpidHeadersSize,
2418   kpidCTime,
2419 
2420   kpidSectorSize,
2421   kpidId
2422   // kpidSectorsPerTrack,
2423   // kpidNumHeads,
2424   // kpidHiddenSectors
2425 };
2426 */
2427 
2428 IMP_IInArchive_Props
2429 IMP_IInArchive_ArcProps_WITH_NAME
2430 
2431 static void NtfsTimeToProp(UInt64 t, NCOM::CPropVariant &prop)
2432 {
2433   FILETIME ft;
2434   ft.dwLowDateTime = (DWORD)t;
2435   ft.dwHighDateTime = (DWORD)(t >> 32);
2436   prop = ft;
2437 }
2438 
2439 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
2440 {
2441   COM_TRY_BEGIN
2442   NCOM::CPropVariant prop;
2443 
2444   const CMftRec *volRec = (Recs.Size() > kRecIndex_Volume ? &Recs[kRecIndex_Volume] : NULL);
2445 
2446   switch (propID)
2447   {
2448     case kpidClusterSize: prop = Header.ClusterSize(); break;
2449     case kpidPhySize: prop = PhySize; break;
2450     /*
2451     case kpidHeadersSize:
2452     {
2453       UInt64 val = 0;
2454       for (unsigned i = 0; i < kNumSysRecs; i++)
2455       {
2456         printf("\n%2d: %8I64d ", i, Recs[i].GetPackSize());
2457         if (i == 8)
2458           i = i
2459         val += Recs[i].GetPackSize();
2460       }
2461       prop = val;
2462       break;
2463     }
2464     */
2465     case kpidCTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.CTime, prop); break;
2466     case kpidMTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.MTime, prop); break;
2467     case kpidShortComment:
2468     case kpidVolumeName:
2469     {
2470       FOR_VECTOR (i, VolAttrs)
2471       {
2472         const CAttr &attr = VolAttrs[i];
2473         if (attr.Type == ATTR_TYPE_VOLUME_NAME)
2474         {
2475           UString2 name;
2476           GetString(attr.Data, (unsigned)attr.Data.Size() / 2, name);
2477           if (!name.IsEmpty())
2478             prop = name.GetRawPtr();
2479           break;
2480         }
2481       }
2482       break;
2483     }
2484     case kpidFileSystem:
2485     {
2486       AString s ("NTFS");
2487       FOR_VECTOR (i, VolAttrs)
2488       {
2489         const CAttr &attr = VolAttrs[i];
2490         if (attr.Type == ATTR_TYPE_VOLUME_INFO)
2491         {
2492           CVolInfo vi;
2493           if (attr.ParseVolInfo(vi))
2494           {
2495             s.Add_Space();
2496             s.Add_UInt32(vi.MajorVer);
2497             s.Add_Dot();
2498             s.Add_UInt32(vi.MinorVer);
2499           }
2500           break;
2501         }
2502       }
2503       prop = s;
2504       break;
2505     }
2506     case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
2507     case kpidRecordSize: prop = (UInt32)1 << Header.MftRecordSizeLog; break;
2508     case kpidId: prop = Header.SerialNumber; break;
2509 
2510     case kpidIsTree: prop = true; break;
2511     case kpidIsDeleted: prop = _showDeletedFiles; break;
2512     case kpidIsAltStream: prop = ThereAreAltStreams; break;
2513     case kpidIsAux: prop = true; break;
2514     case kpidINode: prop = true; break;
2515 
2516     case kpidWarning:
2517       if (_lostFolderIndex_Normal >= 0)
2518         prop = "There are lost files";
2519       break;
2520 
2521     /*
2522     case kpidWarningFlags:
2523     {
2524       UInt32 flags = 0;
2525       if (_headerWarning)
2526         flags |= k_ErrorFlags_HeadersError;
2527       if (flags != 0)
2528         prop = flags;
2529       break;
2530     }
2531     */
2532 
2533     // case kpidMediaType: prop = Header.MediaType; break;
2534     // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
2535     // case kpidNumHeads: prop = Header.NumHeads; break;
2536     // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
2537   }
2538   prop.Detach(value);
2539   return S_OK;
2540   COM_TRY_END
2541 }
2542 
2543 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
2544 {
2545   COM_TRY_BEGIN
2546   NCOM::CPropVariant prop;
2547   if (index >= Items.Size())
2548   {
2549     switch (propID)
2550     {
2551       case kpidName:
2552       case kpidPath:
2553         prop = VirtFolderNames[index - Items.Size()].GetRawPtr();
2554         break;
2555       case kpidIsDir: prop = true; break;
2556       case kpidIsAux: prop = true; break;
2557       case kpidIsDeleted:
2558         if ((int)index == _lostFolderIndex_Deleted)
2559           prop = true;
2560         break;
2561     }
2562     prop.Detach(value);
2563     return S_OK;
2564   }
2565 
2566   const CItem &item = Items[index];
2567   const CMftRec &rec = Recs[item.RecIndex];
2568 
2569   const CAttr *data= NULL;
2570   if (item.DataIndex >= 0)
2571     data = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start];
2572 
2573   // const CFileNameAttr *fn = &rec.FileNames[item.NameIndex];
2574   /*
2575   if (rec.FileNames.Size() > 0)
2576     fn = &rec.FileNames[0];
2577   */
2578 
2579   switch (propID)
2580   {
2581     case kpidPath:
2582       GetItemPath(index, prop);
2583       break;
2584 
2585     /*
2586     case kpidLink:
2587       if (!rec.ReparseAttr.SubsName.IsEmpty())
2588       {
2589         prop = rec.ReparseAttr.SubsName;
2590       }
2591       break;
2592     case kpidLink2:
2593       if (!rec.ReparseAttr.PrintName.IsEmpty())
2594       {
2595         prop = rec.ReparseAttr.PrintName;
2596       }
2597       break;
2598 
2599     case kpidLinkType:
2600       if (rec.ReparseAttr.Tag != 0)
2601       {
2602         prop = (rec.ReparseAttr.Tag & 0xFFFF);
2603       }
2604       break;
2605     */
2606 
2607     case kpidINode:
2608     {
2609       // const CMftRec &rec = Recs[item.RecIndex];
2610       // prop = ((UInt64)rec.SeqNumber << 48) | item.RecIndex;
2611       prop = (UInt32)item.RecIndex;
2612       break;
2613     }
2614     case kpidStreamId:
2615     {
2616       if (item.DataIndex >= 0)
2617         prop = ((UInt64)item.RecIndex << 32) | (unsigned)item.DataIndex;
2618       break;
2619     }
2620 
2621     case kpidName:
2622     {
2623       const UString2 *s;
2624       if (item.IsAltStream())
2625         s = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start].Name;
2626       else
2627         s = &rec.FileNames[item.NameIndex].Name;
2628       if (s->IsEmpty())
2629         prop = (const wchar_t *)EmptyString;
2630       else
2631         prop = s->GetRawPtr();
2632       break;
2633     }
2634 
2635     case kpidShortName:
2636     {
2637       if (!item.IsAltStream())
2638       {
2639         int dosNameIndex = rec.FindDosName(item.NameIndex);
2640         if (dosNameIndex >= 0)
2641         {
2642           const UString2 &s = rec.FileNames[dosNameIndex].Name;
2643           if (s.IsEmpty())
2644             prop = (const wchar_t *)EmptyString;
2645           else
2646             prop = s.GetRawPtr();
2647         }
2648       }
2649       break;
2650     }
2651 
2652     case kpidIsDir: prop = item.IsDir(); break;
2653     case kpidIsAltStream: prop = item.IsAltStream(); break;
2654     case kpidIsDeleted: prop = !rec.InUse(); break;
2655     case kpidIsAux: prop = false; break;
2656 
2657     case kpidMTime: NtfsTimeToProp(rec.SiAttr.MTime, prop); break;
2658     case kpidCTime: NtfsTimeToProp(rec.SiAttr.CTime, prop); break;
2659     case kpidATime: NtfsTimeToProp(rec.SiAttr.ATime, prop); break;
2660     case kpidChangeTime: NtfsTimeToProp(rec.SiAttr.ThisRecMTime, prop); break;
2661 
2662     /*
2663     case kpidMTime2: if (fn) NtfsTimeToProp(fn->MTime, prop); break;
2664     case kpidCTime2: if (fn) NtfsTimeToProp(fn->CTime, prop); break;
2665     case kpidATime2: if (fn) NtfsTimeToProp(fn->ATime, prop); break;
2666     case kpidRecMTime2: if (fn) NtfsTimeToProp(fn->ThisRecMTime, prop); break;
2667     */
2668 
2669     case kpidAttrib:
2670     {
2671       UInt32 attrib;
2672       /* WinXP-64: The CFileNameAttr::Attrib is not updated  after some changes. Why?
2673          CSiAttr:attrib is updated better. So we use CSiAttr:Sttrib */
2674       /*
2675       if (fn)
2676         attrib = fn->Attrib;
2677       else
2678       */
2679         attrib = rec.SiAttr.Attrib;
2680       if (item.IsDir())
2681         attrib |= FILE_ATTRIBUTE_DIRECTORY;
2682 
2683       /* some system entries can contain extra flags (Index View).
2684       // 0x10000000   (Directory)
2685       // 0x20000000   FILE_ATTR_VIEW_INDEX_PRESENT MFT_RECORD_IS_VIEW_INDEX (Index View)
2686       But we don't need them */
2687       attrib &= 0xFFFF;
2688 
2689       prop = attrib;
2690       break;
2691     }
2692     case kpidLinks: if (rec.MyNumNameLinks != 1) prop = rec.MyNumNameLinks; break;
2693 
2694     case kpidNumAltStreams:
2695     {
2696       if (!item.IsAltStream())
2697       {
2698         unsigned num = rec.DataRefs.Size();
2699         if (num > 0)
2700         {
2701           if (!rec.IsDir() && rec.DataAttrs[rec.DataRefs[0].Start].Name.IsEmpty())
2702             num--;
2703           if (num > 0)
2704             prop = (UInt32)num;
2705         }
2706       }
2707       break;
2708     }
2709 
2710     case kpidSize: if (data) prop = data->GetSize(); else if (!item.IsDir()) prop = (UInt64)0; break;
2711     case kpidPackSize: if (data) prop = data->GetPackSize(); else if (!item.IsDir()) prop = (UInt64)0; break;
2712     case kpidNumBlocks: if (data) prop = (UInt32)rec.GetNumExtents(item.DataIndex, Header.ClusterSizeLog, Header.NumClusters); break;
2713   }
2714   prop.Detach(value);
2715   return S_OK;
2716   COM_TRY_END
2717 }
2718 
2719 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback))
2720 {
2721   COM_TRY_BEGIN
2722   {
2723     OpenCallback = callback;
2724     InStream = stream;
2725     HRESULT res;
2726     try
2727     {
2728       res = CDatabase::Open();
2729       if (res == S_OK)
2730         return S_OK;
2731     }
2732     catch(...)
2733     {
2734       Close();
2735       throw;
2736     }
2737     Close();
2738     return res;
2739   }
2740   COM_TRY_END
2741 }
2742 
2743 Z7_COM7F_IMF(CHandler::Close())
2744 {
2745   ClearAndClose();
2746   return S_OK;
2747 }
2748 
2749 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2750     Int32 testMode, IArchiveExtractCallback *extractCallback))
2751 {
2752   COM_TRY_BEGIN
2753   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2754   if (allFilesMode)
2755     numItems = Items.Size();
2756   if (numItems == 0)
2757     return S_OK;
2758   UInt32 i;
2759   UInt64 totalSize = 0;
2760   for (i = 0; i < numItems; i++)
2761   {
2762     const UInt32 index = allFilesMode ? i : indices[i];
2763     if (index >= (UInt32)Items.Size())
2764       continue;
2765     const CItem &item = Items[allFilesMode ? i : indices[i]];
2766     const CMftRec &rec = Recs[item.RecIndex];
2767     if (item.DataIndex >= 0)
2768       totalSize += rec.GetSize((unsigned)item.DataIndex);
2769   }
2770   RINOK(extractCallback->SetTotal(totalSize))
2771 
2772   UInt64 totalPackSize;
2773   totalSize = totalPackSize = 0;
2774 
2775   UInt32 clusterSize = Header.ClusterSize();
2776   CByteBuffer buf(clusterSize);
2777 
2778   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
2779   lps->Init(extractCallback, false);
2780   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
2781   CMyComPtr2_Create<ISequentialOutStream, CDummyOutStream> outStream;
2782 
2783   for (i = 0;; i++)
2784   {
2785     lps->InSize = totalPackSize;
2786     lps->OutSize = totalSize;
2787     RINOK(lps->SetCur())
2788     if (i >= numItems)
2789       break;
2790 
2791     CMyComPtr<ISequentialOutStream> realOutStream;
2792     const Int32 askMode = testMode ?
2793         NExtract::NAskMode::kTest :
2794         NExtract::NAskMode::kExtract;
2795     const UInt32 index = allFilesMode ? i : indices[i];
2796     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
2797 
2798     if (index >= (UInt32)Items.Size() || Items[index].IsDir())
2799     {
2800       RINOK(extractCallback->PrepareOperation(askMode))
2801       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
2802       continue;
2803     }
2804 
2805     const CItem &item = Items[index];
2806 
2807     if (!testMode && !realOutStream)
2808       continue;
2809     RINOK(extractCallback->PrepareOperation(askMode))
2810 
2811     outStream->SetStream(realOutStream);
2812     realOutStream.Release();
2813     outStream->Init();
2814 
2815     const CMftRec &rec = Recs[item.RecIndex];
2816 
2817     int res = NExtract::NOperationResult::kDataError;
2818     {
2819       CMyComPtr<IInStream> inStream;
2820       HRESULT hres = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &inStream);
2821       if (hres == S_FALSE)
2822         res = NExtract::NOperationResult::kUnsupportedMethod;
2823       else
2824       {
2825         RINOK(hres)
2826         if (inStream)
2827         {
2828           hres = copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps);
2829           if (hres != S_OK &&  hres != S_FALSE)
2830           {
2831             RINOK(hres)
2832           }
2833           if (/* copyCoderSpec->TotalSize == item.GetSize() && */ hres == S_OK)
2834             res = NExtract::NOperationResult::kOK;
2835         }
2836       }
2837     }
2838     if (item.DataIndex >= 0)
2839     {
2840       const CAttr &data = rec.DataAttrs[rec.DataRefs[item.DataIndex].Start];
2841       totalPackSize += data.GetPackSize();
2842       totalSize += data.GetSize();
2843     }
2844     outStream->ReleaseStream();
2845     RINOK(extractCallback->SetOperationResult(res))
2846   }
2847   return S_OK;
2848   COM_TRY_END
2849 }
2850 
2851 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
2852 {
2853   *numItems = Items.Size() + VirtFolderNames.Size();
2854   return S_OK;
2855 }
2856 
2857 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
2858 {
2859   InitProps();
2860 
2861   for (UInt32 i = 0; i < numProps; i++)
2862   {
2863     const wchar_t *name = names[i];
2864     const PROPVARIANT &prop = values[i];
2865 
2866     if (StringsAreEqualNoCase_Ascii(name, "ld"))
2867     {
2868       RINOK(PROPVARIANT_to_bool(prop, _showDeletedFiles))
2869     }
2870     else if (StringsAreEqualNoCase_Ascii(name, "ls"))
2871     {
2872       RINOK(PROPVARIANT_to_bool(prop, _showSystemFiles))
2873     }
2874     else if (IsString1PrefixedByString2_NoCase_Ascii(name, "mt"))
2875     {
2876     }
2877     else if (IsString1PrefixedByString2_NoCase_Ascii(name, "memuse"))
2878     {
2879     }
2880     else
2881       return E_INVALIDARG;
2882   }
2883   return S_OK;
2884 }
2885 
2886 static const Byte k_Signature[] = { 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' ', 0 };
2887 
2888 REGISTER_ARC_I(
2889   "NTFS", "ntfs img", NULL, 0xD9,
2890   k_Signature,
2891   3,
2892   0,
2893   NULL)
2894 
2895 }}
2896