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