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 *const *)elem1));
390   const CAttr &a2 = *(*((const CAttr *const *)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             const size_t offs = 1 + dist;
721             Byte *p = dest + destSize - offs;
722             destSize += len;
723             sbOffset += len;
724             const Byte *lim = p + len;
725             p[offs] = *p; ++p;
726             p[offs] = *p; ++p;
727             do
728               p[offs] = *p;
729             while (++p != lim);
730           }
731         }
732       }
733       while (comprSize > 0);
734       src += pos;
735     }
736   }
737   return destSize;
738 }
739 
Read(void * data,UInt32 size,UInt32 * processedSize)740 STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
741 {
742   if (processedSize)
743     *processedSize = 0;
744   if (_virtPos >= Size)
745     return (Size == _virtPos) ? S_OK: E_FAIL;
746   if (size == 0)
747     return S_OK;
748   {
749     const UInt64 rem = Size - _virtPos;
750     if (size > rem)
751       size = (UInt32)rem;
752   }
753 
754   if (_virtPos >= InitializedSize)
755   {
756     memset((Byte *)data, 0, size);
757     _virtPos += size;
758     *processedSize = size;
759     return S_OK;
760   }
761 
762   {
763     const UInt64 rem = InitializedSize - _virtPos;
764     if (size > rem)
765       size = (UInt32)rem;
766   }
767 
768   while (_curRem == 0)
769   {
770     const UInt64 cacheTag = _virtPos >> _chunkSizeLog;
771     const size_t cacheIndex = (size_t)cacheTag & (kNumCacheChunks - 1);
772 
773     if (_tags[cacheIndex] == cacheTag)
774     {
775       const size_t chunkSize = (size_t)1 << _chunkSizeLog;
776       const size_t offset = (size_t)_virtPos & (chunkSize - 1);
777       size_t cur = chunkSize - offset;
778       if (cur > size)
779         cur = size;
780       memcpy(data, _outBuf + (cacheIndex << _chunkSizeLog) + offset, cur);
781       *processedSize = (UInt32)cur;
782       _virtPos += cur;
783       return S_OK;
784     }
785 
786     PRF2(printf("\nVirtPos = %6d", _virtPos));
787 
788     const UInt32 comprUnitSize = (UInt32)1 << CompressionUnit;
789     const UInt64 virtBlock = _virtPos >> BlockSizeLog;
790     const UInt64 virtBlock2 = virtBlock & ~((UInt64)comprUnitSize - 1);
791 
792     unsigned left = 0, right = Extents.Size();
793     for (;;)
794     {
795       unsigned mid = (left + right) / 2;
796       if (mid == left)
797         break;
798       if (virtBlock2 < Extents[mid].Virt)
799         right = mid;
800       else
801         left = mid;
802     }
803 
804     bool isCompressed = false;
805     const UInt64 virtBlock2End = virtBlock2 + comprUnitSize;
806     if (CompressionUnit != 0)
807       for (unsigned i = left; i < Extents.Size(); i++)
808       {
809         const CExtent &e = Extents[i];
810         if (e.Virt >= virtBlock2End)
811           break;
812         if (e.IsEmpty())
813         {
814           isCompressed = true;
815           break;
816         }
817       }
818 
819     unsigned i;
820     for (i = left; Extents[i + 1].Virt <= virtBlock; i++);
821 
822     _sparseMode = false;
823     if (!isCompressed)
824     {
825       const CExtent &e = Extents[i];
826       UInt64 newPos = (e.Phy << BlockSizeLog) + _virtPos - (e.Virt << BlockSizeLog);
827       if (newPos != _physPos)
828       {
829         _physPos = newPos;
830         RINOK(SeekToPhys());
831       }
832       UInt64 next = Extents[i + 1].Virt;
833       if (next > virtBlock2End)
834         next &= ~((UInt64)comprUnitSize - 1);
835       next <<= BlockSizeLog;
836       if (next > Size)
837         next = Size;
838       _curRem = next - _virtPos;
839       break;
840     }
841 
842     bool thereArePhy = false;
843 
844     for (unsigned i2 = left; i2 < Extents.Size(); i2++)
845     {
846       const CExtent &e = Extents[i2];
847       if (e.Virt >= virtBlock2End)
848         break;
849       if (!e.IsEmpty())
850       {
851         thereArePhy = true;
852         break;
853       }
854     }
855 
856     if (!thereArePhy)
857     {
858       _curRem = (Extents[i + 1].Virt << BlockSizeLog) - _virtPos;
859       _sparseMode = true;
860       break;
861     }
862 
863     size_t offs = 0;
864     UInt64 curVirt = virtBlock2;
865 
866     for (i = left; i < Extents.Size(); i++)
867     {
868       const CExtent &e = Extents[i];
869       if (e.IsEmpty())
870         break;
871       if (e.Virt >= virtBlock2End)
872         return S_FALSE;
873       UInt64 newPos = (e.Phy + (curVirt - e.Virt)) << BlockSizeLog;
874       if (newPos != _physPos)
875       {
876         _physPos = newPos;
877         RINOK(SeekToPhys());
878       }
879       UInt64 numChunks = Extents[i + 1].Virt - curVirt;
880       if (curVirt + numChunks > virtBlock2End)
881         numChunks = virtBlock2End - curVirt;
882       size_t compressed = (size_t)numChunks << BlockSizeLog;
883       RINOK(ReadStream_FALSE(Stream, _inBuf + offs, compressed));
884       curVirt += numChunks;
885       _physPos += compressed;
886       offs += compressed;
887     }
888 
889     size_t destLenMax = GetCuSize();
890     size_t destLen = destLenMax;
891     const UInt64 rem = Size - (virtBlock2 << BlockSizeLog);
892     if (destLen > rem)
893       destLen = (size_t)rem;
894 
895     Byte *dest = _outBuf + (cacheIndex << _chunkSizeLog);
896     size_t destSizeRes = Lznt1Dec(dest, destLenMax, destLen, _inBuf, offs);
897     _tags[cacheIndex] = cacheTag;
898 
899     // some files in Vista have destSize > destLen
900     if (destSizeRes < destLen)
901     {
902       memset(dest, 0, destLenMax);
903       if (InUse)
904         return S_FALSE;
905     }
906   }
907 
908   if (size > _curRem)
909     size = (UInt32)_curRem;
910   HRESULT res = S_OK;
911   if (_sparseMode)
912     memset(data, 0, size);
913   else
914   {
915     res = Stream->Read(data, size, &size);
916     _physPos += size;
917   }
918   if (processedSize)
919     *processedSize = size;
920   _virtPos += size;
921   _curRem -= size;
922   return res;
923 }
924 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)925 STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
926 {
927   switch (seekOrigin)
928   {
929     case STREAM_SEEK_SET: break;
930     case STREAM_SEEK_CUR: offset += _virtPos; break;
931     case STREAM_SEEK_END: offset += Size; break;
932     default: return STG_E_INVALIDFUNCTION;
933   }
934   if (offset < 0)
935     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
936   if (_virtPos != (UInt64)offset)
937   {
938     _curRem = 0;
939     _virtPos = offset;
940   }
941   if (newPosition)
942     *newPosition = offset;
943   return S_OK;
944 }
945 
DataParseExtents(unsigned clusterSizeLog,const CObjectVector<CAttr> & attrs,unsigned attrIndex,unsigned attrIndexLim,UInt64 numPhysClusters,CRecordVector<CExtent> & Extents)946 static HRESULT DataParseExtents(unsigned clusterSizeLog, const CObjectVector<CAttr> &attrs,
947     unsigned attrIndex, unsigned attrIndexLim, UInt64 numPhysClusters, CRecordVector<CExtent> &Extents)
948 {
949   {
950     CExtent e;
951     e.Virt = 0;
952     e.Phy = kEmptyExtent;
953     Extents.Add(e);
954   }
955 
956   const CAttr &attr0 = attrs[attrIndex];
957 
958   /*
959   if (attrs[attrIndexLim - 1].HighVcn + 1 != (attr0.AllocatedSize >> clusterSizeLog))
960   {
961   }
962   */
963 
964   if (attr0.AllocatedSize < attr0.Size ||
965       (attrs[attrIndexLim - 1].HighVcn + 1) != (attr0.AllocatedSize >> clusterSizeLog) ||
966       (attr0.AllocatedSize & ((1 << clusterSizeLog) - 1)) != 0)
967     return S_FALSE;
968 
969   for (unsigned i = attrIndex; i < attrIndexLim; i++)
970     if (!attrs[i].ParseExtents(Extents, numPhysClusters, attr0.CompressionUnit))
971       return S_FALSE;
972 
973   UInt64 packSizeCalc = 0;
974   FOR_VECTOR (k, Extents)
975   {
976     CExtent &e = Extents[k];
977     if (!e.IsEmpty())
978       packSizeCalc += (Extents[k + 1].Virt - e.Virt) << clusterSizeLog;
979     PRF2(printf("\nSize = %4I64X", Extents[k + 1].Virt - e.Virt));
980     PRF2(printf("  Pos = %4I64X", e.Phy));
981   }
982 
983   if (attr0.CompressionUnit != 0)
984   {
985     if (packSizeCalc != attr0.PackSize)
986       return S_FALSE;
987   }
988   else
989   {
990     if (packSizeCalc != attr0.AllocatedSize)
991       return S_FALSE;
992   }
993   return S_OK;
994 }
995 
996 struct CDataRef
997 {
998   unsigned Start;
999   unsigned Num;
1000 };
1001 
1002 static const UInt32 kMagic_FILE = 0x454C4946;
1003 static const UInt32 kMagic_BAAD = 0x44414142;
1004 
1005 struct CMftRec
1006 {
1007   UInt32 Magic;
1008   // UInt64 Lsn;
1009   UInt16 SeqNumber;  // Number of times this mft record has been reused
1010   UInt16 Flags;
1011   // UInt16 LinkCount;
1012   // UInt16 NextAttrInstance;
1013   CMftRef BaseMftRef;
1014   // UInt32 ThisRecNumber;
1015 
1016   UInt32 MyNumNameLinks;
1017   int MyItemIndex; // index in Items[] of main item  for that record, or -1 if there is no item for that record
1018 
1019   CObjectVector<CAttr> DataAttrs;
1020   CObjectVector<CFileNameAttr> FileNames;
1021   CRecordVector<CDataRef> DataRefs;
1022   // CAttr SecurityAttr;
1023 
1024   CSiAttr SiAttr;
1025 
1026   CByteBuffer ReparseData;
1027 
FindWin32Name_for_DosNameNArchive::Ntfs::CMftRec1028   int FindWin32Name_for_DosName(unsigned dosNameIndex) const
1029   {
1030     const CFileNameAttr &cur = FileNames[dosNameIndex];
1031     if (cur.IsDos())
1032       for (unsigned i = 0; i < FileNames.Size(); i++)
1033       {
1034         const CFileNameAttr &next = FileNames[i];
1035         if (next.IsWin32() && cur.ParentDirRef.Val == next.ParentDirRef.Val)
1036           return i;
1037       }
1038     return -1;
1039   }
1040 
FindDosNameNArchive::Ntfs::CMftRec1041   int FindDosName(unsigned nameIndex) const
1042   {
1043     const CFileNameAttr &cur = FileNames[nameIndex];
1044     if (cur.IsWin32())
1045       for (unsigned i = 0; i < FileNames.Size(); i++)
1046       {
1047         const CFileNameAttr &next = FileNames[i];
1048         if (next.IsDos() && cur.ParentDirRef.Val == next.ParentDirRef.Val)
1049           return i;
1050       }
1051     return -1;
1052   }
1053 
1054   /*
1055   bool IsAltStream(int dataIndex) const
1056   {
1057     return dataIndex >= 0 && (
1058       (IsDir() ||
1059       !DataAttrs[DataRefs[dataIndex].Start].Name.IsEmpty()));
1060   }
1061   */
1062 
MoveAttrsFromNArchive::Ntfs::CMftRec1063   void MoveAttrsFrom(CMftRec &src)
1064   {
1065     DataAttrs += src.DataAttrs;
1066     FileNames += src.FileNames;
1067     src.DataAttrs.ClearAndFree();
1068     src.FileNames.ClearAndFree();
1069   }
1070 
GetPackSizeNArchive::Ntfs::CMftRec1071   UInt64 GetPackSize() const
1072   {
1073     UInt64 res = 0;
1074     FOR_VECTOR (i, DataRefs)
1075       res += DataAttrs[DataRefs[i].Start].GetPackSize();
1076     return res;
1077   }
1078 
1079   bool Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 recNumber, CObjectVector<CAttr> *attrs);
1080 
IsEmptyNArchive::Ntfs::CMftRec1081   bool IsEmpty() const { return (Magic <= 2); }
IsFILENArchive::Ntfs::CMftRec1082   bool IsFILE() const { return (Magic == kMagic_FILE); }
IsBAADNArchive::Ntfs::CMftRec1083   bool IsBAAD() const { return (Magic == kMagic_BAAD); }
1084 
InUseNArchive::Ntfs::CMftRec1085   bool InUse() const { return (Flags & 1) != 0; }
IsDirNArchive::Ntfs::CMftRec1086   bool IsDir() const { return (Flags & 2) != 0; }
1087 
1088   void ParseDataNames();
1089   HRESULT GetStream(IInStream *mainStream, int dataIndex,
1090       unsigned clusterSizeLog, UInt64 numPhysClusters, IInStream **stream) const;
1091   unsigned GetNumExtents(int dataIndex, unsigned clusterSizeLog, UInt64 numPhysClusters) const;
1092 
GetSizeNArchive::Ntfs::CMftRec1093   UInt64 GetSize(unsigned dataIndex) const { return DataAttrs[DataRefs[dataIndex].Start].GetSize(); }
1094 
CMftRecNArchive::Ntfs::CMftRec1095   CMftRec(): MyNumNameLinks(0), MyItemIndex(-1) {}
1096 };
1097 
ParseDataNames()1098 void CMftRec::ParseDataNames()
1099 {
1100   DataRefs.Clear();
1101   DataAttrs.Sort(CompareAttr, NULL);
1102 
1103   for (unsigned i = 0; i < DataAttrs.Size();)
1104   {
1105     CDataRef ref;
1106     ref.Start = i;
1107     for (i++; i < DataAttrs.Size(); i++)
1108       if (DataAttrs[ref.Start].Name != DataAttrs[i].Name)
1109         break;
1110     ref.Num = i - ref.Start;
1111     DataRefs.Add(ref);
1112   }
1113 }
1114 
GetStream(IInStream * mainStream,int dataIndex,unsigned clusterSizeLog,UInt64 numPhysClusters,IInStream ** destStream) const1115 HRESULT CMftRec::GetStream(IInStream *mainStream, int dataIndex,
1116     unsigned clusterSizeLog, UInt64 numPhysClusters, IInStream **destStream) const
1117 {
1118   *destStream = 0;
1119   CBufferInStream *streamSpec = new CBufferInStream;
1120   CMyComPtr<IInStream> streamTemp = streamSpec;
1121 
1122   if (dataIndex >= 0)
1123   if ((unsigned)dataIndex < DataRefs.Size())
1124   {
1125     const CDataRef &ref = DataRefs[dataIndex];
1126     unsigned numNonResident = 0;
1127     unsigned i;
1128     for (i = ref.Start; i < ref.Start + ref.Num; i++)
1129       if (DataAttrs[i].NonResident)
1130         numNonResident++;
1131 
1132     const CAttr &attr0 = DataAttrs[ref.Start];
1133 
1134     if (numNonResident != 0 || ref.Num != 1)
1135     {
1136       if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported())
1137         return S_FALSE;
1138       CInStream *ss = new CInStream;
1139       CMyComPtr<IInStream> streamTemp2 = ss;
1140       RINOK(DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, ss->Extents));
1141       ss->Size = attr0.Size;
1142       ss->InitializedSize = attr0.InitializedSize;
1143       ss->Stream = mainStream;
1144       ss->BlockSizeLog = clusterSizeLog;
1145       ss->InUse = InUse();
1146       RINOK(ss->InitAndSeek(attr0.CompressionUnit));
1147       *destStream = streamTemp2.Detach();
1148       return S_OK;
1149     }
1150 
1151     streamSpec->Buf = attr0.Data;
1152   }
1153 
1154   streamSpec->Init();
1155   *destStream = streamTemp.Detach();
1156   return S_OK;
1157 }
1158 
GetNumExtents(int dataIndex,unsigned clusterSizeLog,UInt64 numPhysClusters) const1159 unsigned CMftRec::GetNumExtents(int dataIndex, unsigned clusterSizeLog, UInt64 numPhysClusters) const
1160 {
1161   if (dataIndex < 0)
1162     return 0;
1163   {
1164     const CDataRef &ref = DataRefs[dataIndex];
1165     unsigned numNonResident = 0;
1166     unsigned i;
1167     for (i = ref.Start; i < ref.Start + ref.Num; i++)
1168       if (DataAttrs[i].NonResident)
1169         numNonResident++;
1170 
1171     const CAttr &attr0 = DataAttrs[ref.Start];
1172 
1173     if (numNonResident != 0 || ref.Num != 1)
1174     {
1175       if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported())
1176         return 0; // error;
1177       CRecordVector<CExtent> extents;
1178       if (DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, extents) != S_OK)
1179         return 0; // error;
1180       return extents.Size() - 1;
1181     }
1182     // if (attr0.Data.Size() != 0)
1183     //   return 1;
1184     return 0;
1185   }
1186 }
1187 
Parse(Byte * p,unsigned sectorSizeLog,UInt32 numSectors,UInt32 recNumber,CObjectVector<CAttr> * attrs)1188 bool CMftRec::Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 recNumber,
1189     CObjectVector<CAttr> *attrs)
1190 {
1191   G32(p, Magic);
1192   if (!IsFILE())
1193     return IsEmpty() || IsBAAD();
1194 
1195 
1196   {
1197     UInt32 usaOffset;
1198     UInt32 numUsaItems;
1199     G16(p + 0x04, usaOffset);
1200     G16(p + 0x06, numUsaItems);
1201 
1202     /* NTFS stores (usn) to 2 last bytes in each sector (before writing record to disk).
1203        Original values of these two bytes are stored in table.
1204        So we restore original data from table */
1205 
1206     if ((usaOffset & 1) != 0
1207         || usaOffset + numUsaItems * 2 > ((UInt32)1 << sectorSizeLog) - 2
1208         || numUsaItems == 0
1209         || numUsaItems - 1 != numSectors)
1210       return false;
1211 
1212     if (usaOffset >= 0x30) // NTFS 3.1+
1213     {
1214       UInt32 iii = Get32(p + 0x2C);
1215       if (iii != recNumber)
1216       {
1217         // ntfs-3g probably writes 0 (that probably is incorrect value) to this field for unused records.
1218         // so we support that "bad" case.
1219         if (iii != 0)
1220           return false;
1221       }
1222     }
1223 
1224     UInt16 usn = Get16(p + usaOffset);
1225     // PRF(printf("\nusn = %d", usn));
1226     for (UInt32 i = 1; i < numUsaItems; i++)
1227     {
1228       void *pp = p + (i << sectorSizeLog) - 2;
1229       if (Get16(pp) != usn)
1230         return false;
1231       SetUi16(pp, Get16(p + usaOffset + i * 2));
1232     }
1233   }
1234 
1235   // G64(p + 0x08, Lsn);
1236   G16(p + 0x10, SeqNumber);
1237   // G16(p + 0x12, LinkCount);
1238   // PRF(printf(" L=%d", LinkCount));
1239   UInt32 attrOffs = Get16(p + 0x14);
1240   G16(p + 0x16, Flags);
1241   PRF(printf(" F=%4X", Flags));
1242 
1243   UInt32 bytesInUse = Get32(p + 0x18);
1244   UInt32 bytesAlloc = Get32(p + 0x1C);
1245   G64(p + 0x20, BaseMftRef.Val);
1246   if (BaseMftRef.Val != 0)
1247   {
1248     PRF(printf("  BaseRef=%d", (int)BaseMftRef.Val));
1249     // return false; // Check it;
1250   }
1251   // G16(p + 0x28, NextAttrInstance);
1252 
1253   UInt32 limit = numSectors << sectorSizeLog;
1254   if (attrOffs >= limit
1255       || (attrOffs & 7) != 0
1256       || (bytesInUse & 7) != 0
1257       || bytesInUse > limit
1258       || bytesAlloc != limit)
1259     return false;
1260 
1261   limit = bytesInUse;
1262 
1263   for (UInt32 t = attrOffs;;)
1264   {
1265     if (t >= limit)
1266       return false;
1267 
1268     CAttr attr;
1269     // PRF(printf("\n  %2d:", Attrs.Size()));
1270     PRF(printf("\n"));
1271     UInt32 len = attr.Parse(p + t, limit - t);
1272     if (len == 0 || limit - t < len)
1273       return false;
1274     t += len;
1275     if (attr.Type == 0xFFFFFFFF)
1276     {
1277       if (t != limit)
1278         return false;
1279       break;
1280     }
1281     switch (attr.Type)
1282     {
1283       case ATTR_TYPE_FILE_NAME:
1284       {
1285         CFileNameAttr fna;
1286         if (!attr.ParseFileName(fna))
1287           return false;
1288         FileNames.Add(fna);
1289         PRF(printf("  flags = %4x\n  ", (int)fna.NameType));
1290         PRF_UTF16(fna.Name);
1291         break;
1292       }
1293       case ATTR_TYPE_STANDARD_INFO:
1294         if (!attr.ParseSi(SiAttr))
1295           return false;
1296         break;
1297       case ATTR_TYPE_DATA:
1298         DataAttrs.Add(attr);
1299         break;
1300       case ATTR_TYPE_REPARSE_POINT:
1301         ReparseData = attr.Data;
1302         break;
1303       /*
1304       case ATTR_TYPE_SECURITY_DESCRIPTOR:
1305         SecurityAttr = attr;
1306         break;
1307       */
1308       default:
1309         if (attrs)
1310           attrs->Add(attr);
1311         break;
1312     }
1313   }
1314 
1315   return true;
1316 }
1317 
1318 /*
1319   NTFS probably creates empty DATA_ATTRIBUTE for empty file,
1320   But it doesn't do it for
1321     $Secure (:$SDS),
1322     $Extend\$Quota
1323     $Extend\$ObjId
1324     $Extend\$Reparse
1325 */
1326 
1327 static const int k_Item_DataIndex_IsEmptyFile = -1; // file without unnamed data stream
1328 static const int k_Item_DataIndex_IsDir = -2;
1329 
1330 // static const int k_ParentFolderIndex_Root = -1;
1331 static const int k_ParentFolderIndex_Lost = -2;
1332 static const int k_ParentFolderIndex_Deleted = -3;
1333 
1334 struct CItem
1335 {
1336   unsigned RecIndex;  // index in Recs array
1337   unsigned NameIndex; // index in CMftRec::FileNames
1338 
1339   int DataIndex;      /* index in CMftRec::DataRefs
1340                          -1: file without unnamed data stream
1341                          -2: for directories */
1342 
1343   int ParentFolder;   /* index in Items array
1344                          -1: for root items
1345                          -2: [LOST] folder
1346                          -3: [UNKNOWN] folder (deleted lost) */
1347   int ParentHost;     /* index in Items array, if it's AltStream
1348                          -1: if it's not AltStream */
1349 
CItemNArchive::Ntfs::CItem1350   CItem(): DataIndex(k_Item_DataIndex_IsDir), ParentFolder(-1), ParentHost(-1) {}
1351 
IsAltStreamNArchive::Ntfs::CItem1352   bool IsAltStream() const { return ParentHost != -1; }
IsDirNArchive::Ntfs::CItem1353   bool IsDir() const { return DataIndex == k_Item_DataIndex_IsDir; }
1354         // check it !!!
1355         // probably NTFS for empty file still creates empty DATA_ATTRIBUTE
1356         // But it doesn't do it for $Secure:$SDS
1357 };
1358 
1359 struct CDatabase
1360 {
1361   CRecordVector<CItem> Items;
1362   CObjectVector<CMftRec> Recs;
1363   CMyComPtr<IInStream> InStream;
1364   CHeader Header;
1365   unsigned RecSizeLog;
1366   UInt64 PhySize;
1367 
1368   IArchiveOpenCallback *OpenCallback;
1369 
1370   CByteBuffer ByteBuf;
1371 
1372   CObjectVector<CAttr> VolAttrs;
1373 
1374   CByteBuffer SecurData;
1375   CRecordVector<size_t> SecurOffsets;
1376 
1377   bool _showSystemFiles;
1378   bool _showDeletedFiles;
1379   CObjectVector<UString2> VirtFolderNames;
1380   UString EmptyString;
1381 
1382   int _systemFolderIndex;
1383   int _lostFolderIndex_Normal;
1384   int _lostFolderIndex_Deleted;
1385 
1386   // bool _headerWarning;
1387 
1388   bool ThereAreAltStreams;
1389 
InitPropsNArchive::Ntfs::CDatabase1390   void InitProps()
1391   {
1392     _showSystemFiles = true;
1393     // we show SystemFiles by default since it's difficult to track $Extend\* system files
1394     // it must be fixed later
1395     _showDeletedFiles = false;
1396   }
1397 
CDatabaseNArchive::Ntfs::CDatabase1398   CDatabase() { InitProps(); }
~CDatabaseNArchive::Ntfs::CDatabase1399   ~CDatabase() { ClearAndClose(); }
1400 
1401   void Clear();
1402   void ClearAndClose();
1403 
1404   void GetItemPath(unsigned index, NCOM::CPropVariant &path) const;
1405   HRESULT Open();
1406 
1407   HRESULT SeekToCluster(UInt64 cluster);
1408 
FindDirItemForMtfRecNArchive::Ntfs::CDatabase1409   int FindDirItemForMtfRec(UInt64 recIndex) const
1410   {
1411     if (recIndex >= Recs.Size())
1412       return -1;
1413     const CMftRec &rec = Recs[(unsigned)recIndex];
1414     if (!rec.IsDir())
1415       return -1;
1416     return rec.MyItemIndex;
1417     /*
1418     unsigned left = 0, right = Items.Size();
1419     while (left != right)
1420     {
1421       unsigned mid = (left + right) / 2;
1422       const CItem &item = Items[mid];
1423       UInt64 midValue = item.RecIndex;
1424       if (recIndex == midValue)
1425       {
1426         // if item is not dir (file or alt stream we don't return it)
1427         // if (item.DataIndex < 0)
1428         if (item.IsDir())
1429           return mid;
1430         right = mid;
1431       }
1432       else if (recIndex < midValue)
1433         right = mid;
1434       else
1435         left = mid + 1;
1436     }
1437     return -1;
1438     */
1439   }
1440 
1441   bool FindSecurityDescritor(UInt32 id, UInt64 &offset, UInt32 &size) const;
1442 
1443   HRESULT ParseSecuritySDS_2();
ParseSecuritySDSNArchive::Ntfs::CDatabase1444   void ParseSecuritySDS()
1445   {
1446     HRESULT res = ParseSecuritySDS_2();
1447     if (res != S_OK)
1448     {
1449       SecurOffsets.Clear();
1450       SecurData.Free();
1451     }
1452   }
1453 
1454 };
1455 
SeekToCluster(UInt64 cluster)1456 HRESULT CDatabase::SeekToCluster(UInt64 cluster)
1457 {
1458   return InStream->Seek(cluster << Header.ClusterSizeLog, STREAM_SEEK_SET, NULL);
1459 }
1460 
Clear()1461 void CDatabase::Clear()
1462 {
1463   Items.Clear();
1464   Recs.Clear();
1465   SecurOffsets.Clear();
1466   SecurData.Free();
1467   VirtFolderNames.Clear();
1468   _systemFolderIndex = -1;
1469   _lostFolderIndex_Normal = -1;
1470   _lostFolderIndex_Deleted = -1;
1471   ThereAreAltStreams = false;
1472   // _headerWarning = false;
1473   PhySize = 0;
1474 }
1475 
ClearAndClose()1476 void CDatabase::ClearAndClose()
1477 {
1478   Clear();
1479   InStream.Release();
1480 }
1481 
1482 
CopyName(wchar_t * dest,const wchar_t * src)1483 static void CopyName(wchar_t *dest, const wchar_t *src)
1484 {
1485   for (;;)
1486   {
1487     wchar_t c = *src++;
1488     // 18.06
1489     if (c == '\\' || c == '/')
1490       c = '_';
1491     *dest++ = c;
1492     if (c == 0)
1493       return;
1494   }
1495 }
1496 
GetItemPath(unsigned index,NCOM::CPropVariant & path) const1497 void CDatabase::GetItemPath(unsigned index, NCOM::CPropVariant &path) const
1498 {
1499   const CItem *item = &Items[index];
1500   unsigned size = 0;
1501   const CMftRec &rec = Recs[item->RecIndex];
1502   size += rec.FileNames[item->NameIndex].Name.Len();
1503 
1504   bool isAltStream = item->IsAltStream();
1505 
1506   if (isAltStream)
1507   {
1508     const CAttr &data = rec.DataAttrs[rec.DataRefs[item->DataIndex].Start];
1509     if (item->RecIndex == kRecIndex_RootDir)
1510     {
1511       wchar_t *s = path.AllocBstr(data.Name.Len() + 1);
1512       s[0] = L':';
1513       if (!data.Name.IsEmpty())
1514         CopyName(s + 1, data.Name.GetRawPtr());
1515       return;
1516     }
1517 
1518     size += data.Name.Len();
1519     size++;
1520   }
1521 
1522   for (unsigned i = 0;; i++)
1523   {
1524     if (i > 256)
1525     {
1526       path = "[TOO-LONG]";
1527       return;
1528     }
1529     const wchar_t *servName;
1530     if (item->RecIndex < kNumSysRecs
1531         /* && item->RecIndex != kRecIndex_RootDir */)
1532       servName = kVirtualFolder_System;
1533     else
1534     {
1535       int index2 = item->ParentFolder;
1536       if (index2 >= 0)
1537       {
1538         item = &Items[index2];
1539         size += Recs[item->RecIndex].FileNames[item->NameIndex].Name.Len() + 1;
1540         continue;
1541       }
1542       if (index2 == -1)
1543         break;
1544       servName = (index2 == k_ParentFolderIndex_Lost) ?
1545           kVirtualFolder_Lost_Normal :
1546           kVirtualFolder_Lost_Deleted;
1547     }
1548     size += MyStringLen(servName) + 1;
1549     break;
1550   }
1551 
1552   wchar_t *s = path.AllocBstr(size);
1553 
1554   item = &Items[index];
1555 
1556   bool needColon = false;
1557   if (isAltStream)
1558   {
1559     const UString2 &name = rec.DataAttrs[rec.DataRefs[item->DataIndex].Start].Name;
1560     if (!name.IsEmpty())
1561     {
1562       size -= name.Len();
1563       CopyName(s + size, name.GetRawPtr());
1564     }
1565     s[--size] = ':';
1566     needColon = true;
1567   }
1568 
1569   {
1570     const UString2 &name = rec.FileNames[item->NameIndex].Name;
1571     unsigned len = name.Len();
1572     if (len != 0)
1573       CopyName(s + size - len, name.GetRawPtr());
1574     if (needColon)
1575       s[size] =  ':';
1576     size -= len;
1577   }
1578 
1579   for (;;)
1580   {
1581     const wchar_t *servName;
1582     if (item->RecIndex < kNumSysRecs
1583         /* && && item->RecIndex != kRecIndex_RootDir */)
1584       servName = kVirtualFolder_System;
1585     else
1586     {
1587       int index2 = item->ParentFolder;
1588       if (index2 >= 0)
1589       {
1590         item = &Items[index2];
1591         const UString2 &name = Recs[item->RecIndex].FileNames[item->NameIndex].Name;
1592         unsigned len = name.Len();
1593         size--;
1594         if (len != 0)
1595         {
1596           size -= len;
1597           CopyName(s + size, name.GetRawPtr());
1598         }
1599         s[size + len] = WCHAR_PATH_SEPARATOR;
1600         continue;
1601       }
1602       if (index2 == -1)
1603         break;
1604       servName = (index2 == k_ParentFolderIndex_Lost) ?
1605           kVirtualFolder_Lost_Normal :
1606           kVirtualFolder_Lost_Deleted;
1607     }
1608     MyStringCopy(s, servName);
1609     s[MyStringLen(servName)] = WCHAR_PATH_SEPARATOR;
1610     break;
1611   }
1612 }
1613 
FindSecurityDescritor(UInt32 item,UInt64 & offset,UInt32 & size) const1614 bool CDatabase::FindSecurityDescritor(UInt32 item, UInt64 &offset, UInt32 &size) const
1615 {
1616   offset = 0;
1617   size = 0;
1618   unsigned left = 0, right = SecurOffsets.Size();
1619   while (left != right)
1620   {
1621     unsigned mid = (left + right) / 2;
1622     size_t offs = SecurOffsets[mid];
1623     UInt32 midValue = Get32(((const Byte *)SecurData) + offs + 4);
1624     if (item == midValue)
1625     {
1626       offset = Get64((const Byte *)SecurData + offs + 8) + 20;
1627       size = Get32((const Byte *)SecurData + offs + 16) - 20;
1628       return true;
1629     }
1630     if (item < midValue)
1631       right = mid;
1632     else
1633       left = mid + 1;
1634   }
1635   return false;
1636 }
1637 
1638 /*
1639 static int CompareIDs(const size_t *p1, const size_t *p2, void *data)
1640 {
1641   UInt32 id1 = Get32(((const Byte *)data) + *p1 + 4);
1642   UInt32 id2 = Get32(((const Byte *)data) + *p2 + 4);
1643   return MyCompare(id1, id2);
1644 }
1645 */
1646 
1647 // security data contains duplication copy after each 256 KB.
1648 static const unsigned kSecureDuplicateStepBits = 18;
1649 
ParseSecuritySDS_2()1650 HRESULT CDatabase::ParseSecuritySDS_2()
1651 {
1652   const Byte *p = SecurData;
1653   size_t size = SecurData.Size();
1654   const size_t kDuplicateStep = (size_t)1 << kSecureDuplicateStepBits;
1655   const size_t kDuplicateMask = kDuplicateStep - 1;
1656   size_t lim = MyMin(size, kDuplicateStep);
1657   UInt32 idPrev = 0;
1658   for (size_t pos = 0; pos < size && size - pos >= 20;)
1659   {
1660     UInt32 id = Get32(p + pos + 4);
1661     UInt64 offs = Get64(p + pos + 8);
1662     UInt32 entrySize = Get32(p + pos + 16);
1663     if (offs == pos && entrySize >= 20 && lim - pos >= entrySize)
1664     {
1665       if (id <= idPrev)
1666         return S_FALSE;
1667       idPrev = id;
1668       SecurOffsets.Add(pos);
1669       pos += entrySize;
1670       pos = (pos + 0xF) & ~(size_t)0xF;
1671       if ((pos & kDuplicateMask) != 0)
1672         continue;
1673     }
1674     else
1675       pos = (pos + kDuplicateStep) & ~kDuplicateMask;
1676     pos += kDuplicateStep;
1677     lim = pos + kDuplicateStep;
1678     if (lim >= size)
1679       lim = size;
1680   }
1681   // we checked that IDs are sorted, so we don't need Sort
1682   // SecurOffsets.Sort(CompareIDs, (void *)p);
1683   return S_OK;
1684 }
1685 
Open()1686 HRESULT CDatabase::Open()
1687 {
1688   Clear();
1689 
1690   /* NTFS layout:
1691      1) main part (as specified by NumClusters). Only that part is available, if we open "\\.\c:"
1692      2) additional empty sectors (as specified by NumSectors)
1693      3) the copy of first sector (boot sector)
1694 
1695      We support both cases:
1696       - the file with only main part
1697       - full file (as raw data on partition), including the copy
1698         of first sector (boot sector) at the end of data
1699 
1700      We don't support the case, when only the copy of boot sector
1701      at the end was detected as NTFS signature.
1702   */
1703 
1704   {
1705     static const UInt32 kHeaderSize = 512;
1706     Byte buf[kHeaderSize];
1707     RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize));
1708     if (!Header.Parse(buf))
1709       return S_FALSE;
1710 
1711     UInt64 fileSize;
1712     RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize));
1713     PhySize = Header.GetPhySize_Clusters();
1714     if (fileSize < PhySize)
1715       return S_FALSE;
1716 
1717     UInt64 phySizeMax = Header.GetPhySize_Max();
1718     if (fileSize >= phySizeMax)
1719     {
1720       RINOK(InStream->Seek(Header.NumSectors << Header.SectorSizeLog, STREAM_SEEK_SET, NULL));
1721       Byte buf2[kHeaderSize];
1722       if (ReadStream_FALSE(InStream, buf2, kHeaderSize) == S_OK)
1723       {
1724         if (memcmp(buf, buf2, kHeaderSize) == 0)
1725           PhySize = phySizeMax;
1726         // else _headerWarning = true;
1727       }
1728     }
1729   }
1730 
1731   SeekToCluster(Header.MftCluster);
1732 
1733   CMftRec mftRec;
1734   UInt32 numSectorsInRec;
1735 
1736   CMyComPtr<IInStream> mftStream;
1737   {
1738     UInt32 blockSize = 1 << 12;
1739     ByteBuf.Alloc(blockSize);
1740     RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize));
1741 
1742     {
1743       UInt32 allocSize = Get32(ByteBuf + 0x1C);
1744       int t = GetLog(allocSize);
1745       if (t < (int)Header.SectorSizeLog)
1746         return S_FALSE;
1747       RecSizeLog = t;
1748       if (RecSizeLog > 15)
1749         return S_FALSE;
1750     }
1751 
1752     numSectorsInRec = 1 << (RecSizeLog - Header.SectorSizeLog);
1753     if (!mftRec.Parse(ByteBuf, Header.SectorSizeLog, numSectorsInRec, 0, NULL))
1754       return S_FALSE;
1755     if (!mftRec.IsFILE())
1756       return S_FALSE;
1757     mftRec.ParseDataNames();
1758     if (mftRec.DataRefs.IsEmpty())
1759       return S_FALSE;
1760     RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream));
1761     if (!mftStream)
1762       return S_FALSE;
1763   }
1764 
1765   // CObjectVector<CAttr> SecurityAttrs;
1766 
1767   UInt64 mftSize = mftRec.DataAttrs[0].Size;
1768   if ((mftSize >> 4) > Header.GetPhySize_Clusters())
1769     return S_FALSE;
1770 
1771   const size_t kBufSize = (1 << 15);
1772   const size_t recSize = ((size_t)1 << RecSizeLog);
1773   if (kBufSize < recSize)
1774     return S_FALSE;
1775 
1776   {
1777     const UInt64 numFiles = mftSize >> RecSizeLog;
1778     if (numFiles > (1 << 30))
1779       return S_FALSE;
1780     if (OpenCallback)
1781     {
1782       RINOK(OpenCallback->SetTotal(&numFiles, &mftSize));
1783     }
1784 
1785     ByteBuf.Alloc(kBufSize);
1786     Recs.ClearAndReserve((unsigned)numFiles);
1787   }
1788 
1789   for (UInt64 pos64 = 0;;)
1790   {
1791     if (OpenCallback)
1792     {
1793       const UInt64 numFiles = Recs.Size();
1794       if ((numFiles & 0x3FF) == 0)
1795       {
1796         RINOK(OpenCallback->SetCompleted(&numFiles, &pos64));
1797       }
1798     }
1799     size_t readSize = kBufSize;
1800     {
1801       const UInt64 rem = mftSize - pos64;
1802       if (readSize > rem)
1803         readSize = (size_t)rem;
1804     }
1805     if (readSize < recSize)
1806       break;
1807     RINOK(ReadStream_FALSE(mftStream, ByteBuf, readSize));
1808     pos64 += readSize;
1809 
1810     for (size_t i = 0; readSize >= recSize; i += recSize, readSize -= recSize)
1811     {
1812       PRF(printf("\n---------------------"));
1813       PRF(printf("\n%5d:", Recs.Size()));
1814 
1815       Byte *p = ByteBuf + i;
1816       CMftRec rec;
1817 
1818       CObjectVector<CAttr> *attrs = NULL;
1819       unsigned recIndex = Recs.Size();
1820       switch (recIndex)
1821       {
1822         case kRecIndex_Volume: attrs = &VolAttrs; break;
1823         // case kRecIndex_Security: attrs = &SecurityAttrs; break;
1824       }
1825 
1826       if (!rec.Parse(p, Header.SectorSizeLog, numSectorsInRec, (UInt32)Recs.Size(), attrs))
1827         return S_FALSE;
1828       Recs.Add(rec);
1829     }
1830   }
1831 
1832   /*
1833   // that code looks too complex. And we can get security info without index parsing
1834   for (i = 0; i < SecurityAttrs.Size(); i++)
1835   {
1836     const CAttr &attr = SecurityAttrs[i];
1837     if (attr.Name == L"$SII")
1838     {
1839       if (attr.Type == ATTR_TYPE_INDEX_ROOT)
1840       {
1841         const Byte *data = attr.Data;
1842         size_t size = attr.Data.Size();
1843 
1844         // Index Root
1845         UInt32 attrType = Get32(data);
1846         UInt32 collationRule = Get32(data + 4);
1847         UInt32 indexAllocationEtrySizeSize = Get32(data + 8);
1848         UInt32 clustersPerIndexRecord = Get32(data + 0xC);
1849         data += 0x10;
1850 
1851         // Index Header
1852         UInt32 firstEntryOffset = Get32(data);
1853         UInt32 totalSize = Get32(data + 4);
1854         UInt32 allocSize = Get32(data + 8);
1855         UInt32 flags = Get32(data + 0xC);
1856 
1857         int num = 0;
1858         for (int j = 0 ; j < num; j++)
1859         {
1860           if (Get32(data) != 0x1414 || // offset and size
1861               Get32(data + 4) != 0 ||
1862               Get32(data + 8) != 0x428) // KeySize / EntrySize
1863             break;
1864           UInt32 flags = Get32(data + 12);
1865           UInt32 id = Get32(data + 0x10);
1866           if (id = Get32(data + 0x18))
1867             break;
1868           UInt32 descriptorOffset = Get64(data + 0x1C);
1869           UInt32 descriptorSize = Get64(data + 0x24);
1870           data += 0x28;
1871         }
1872         // break;
1873       }
1874     }
1875   }
1876   */
1877 
1878   unsigned i;
1879 
1880   for (i = 0; i < Recs.Size(); i++)
1881   {
1882     CMftRec &rec = Recs[i];
1883     if (!rec.BaseMftRef.IsBaseItself())
1884     {
1885       UInt64 refIndex = rec.BaseMftRef.GetIndex();
1886       if (refIndex > (UInt32)Recs.Size())
1887         return S_FALSE;
1888       CMftRec &refRec = Recs[(unsigned)refIndex];
1889       bool moveAttrs = (refRec.SeqNumber == rec.BaseMftRef.GetNumber() && refRec.BaseMftRef.IsBaseItself());
1890       if (rec.InUse() && refRec.InUse())
1891       {
1892         if (!moveAttrs)
1893           return S_FALSE;
1894       }
1895       else if (rec.InUse() || refRec.InUse())
1896         moveAttrs = false;
1897       if (moveAttrs)
1898         refRec.MoveAttrsFrom(rec);
1899     }
1900   }
1901 
1902   for (i = 0; i < Recs.Size(); i++)
1903     Recs[i].ParseDataNames();
1904 
1905   for (i = 0; i < Recs.Size(); i++)
1906   {
1907     CMftRec &rec = Recs[i];
1908     if (!rec.IsFILE() || !rec.BaseMftRef.IsBaseItself())
1909       continue;
1910     if (i < kNumSysRecs && !_showSystemFiles)
1911       continue;
1912     if (!rec.InUse() && !_showDeletedFiles)
1913       continue;
1914 
1915     rec.MyNumNameLinks = rec.FileNames.Size();
1916 
1917     // printf("\n%4d: ", i);
1918 
1919     /* Actually DataAttrs / DataRefs are sorted by name.
1920        It can not be more than one unnamed stream in DataRefs
1921        And indexOfUnnamedStream <= 0.
1922     */
1923 
1924     int indexOfUnnamedStream = -1;
1925     if (!rec.IsDir())
1926     {
1927       FOR_VECTOR (di, rec.DataRefs)
1928         if (rec.DataAttrs[rec.DataRefs[di].Start].Name.IsEmpty())
1929         {
1930           indexOfUnnamedStream = di;
1931           break;
1932         }
1933     }
1934 
1935     if (rec.FileNames.IsEmpty())
1936     {
1937       bool needShow = true;
1938       if (i < kNumSysRecs)
1939       {
1940         needShow = false;
1941         FOR_VECTOR (di, rec.DataRefs)
1942           if (rec.GetSize(di) != 0)
1943           {
1944             needShow = true;
1945             break;
1946           }
1947       }
1948       if (needShow)
1949       {
1950         CFileNameAttr &fna = rec.FileNames.AddNew();
1951         // we set incorrect ParentDirRef, that will place item to [LOST] folder
1952         fna.ParentDirRef.Val = (UInt64)(Int64)-1;
1953         char s[16 + 16];
1954         ConvertUInt32ToString(i, MyStpCpy(s, "[NONAME]-"));
1955         fna.Name.SetFromAscii(s);
1956         fna.NameType = kFileNameType_Win32Dos;
1957         fna.Attrib = 0;
1958       }
1959     }
1960 
1961     // bool isMainName = true;
1962 
1963     FOR_VECTOR (t, rec.FileNames)
1964     {
1965       #ifdef SHOW_DEBUG_INFO
1966       const CFileNameAttr &fna = rec.FileNames[t];
1967       #endif
1968       PRF(printf("\n %4d ", (int)fna.NameType));
1969       PRF_UTF16(fna.Name);
1970       // PRF(printf("  | "));
1971 
1972       if (rec.FindWin32Name_for_DosName(t) >= 0)
1973       {
1974         rec.MyNumNameLinks--;
1975         continue;
1976       }
1977 
1978       CItem item;
1979       item.NameIndex = t;
1980       item.RecIndex = i;
1981       item.DataIndex = rec.IsDir() ?
1982           k_Item_DataIndex_IsDir :
1983             (indexOfUnnamedStream < 0 ?
1984           k_Item_DataIndex_IsEmptyFile :
1985           indexOfUnnamedStream);
1986 
1987       if (rec.MyItemIndex < 0)
1988         rec.MyItemIndex = Items.Size();
1989       item.ParentHost = Items.Add(item);
1990 
1991       /* we can use that code to reduce the number of alt streams:
1992          it will not show how alt streams for hard links. */
1993       // if (!isMainName) continue; isMainName = false;
1994 
1995       unsigned numAltStreams = 0;
1996 
1997       FOR_VECTOR (di, rec.DataRefs)
1998       {
1999         if (!rec.IsDir() && (int)di == indexOfUnnamedStream)
2000           continue;
2001 
2002         const UString2 &subName = rec.DataAttrs[rec.DataRefs[di].Start].Name;
2003 
2004         PRF(printf("\n alt stream: "));
2005         PRF_UTF16(subName);
2006 
2007         {
2008           // $BadClus:$Bad is sparse file for all clusters. So we skip it.
2009           if (i == kRecIndex_BadClus && subName == L"$Bad")
2010             continue;
2011         }
2012 
2013         numAltStreams++;
2014         ThereAreAltStreams = true;
2015         item.DataIndex = di;
2016         Items.Add(item);
2017       }
2018     }
2019   }
2020 
2021   if (Recs.Size() > kRecIndex_Security)
2022   {
2023     const CMftRec &rec = Recs[kRecIndex_Security];
2024     FOR_VECTOR (di, rec.DataRefs)
2025     {
2026       const CAttr &attr = rec.DataAttrs[rec.DataRefs[di].Start];
2027       if (attr.Name == L"$SDS")
2028       {
2029         CMyComPtr<IInStream> sdsStream;
2030         RINOK(rec.GetStream(InStream, di, Header.ClusterSizeLog, Header.NumClusters, &sdsStream));
2031         if (sdsStream)
2032         {
2033           UInt64 size64 = attr.GetSize();
2034           if (size64 < (UInt32)1 << 29)
2035           {
2036             size_t size = (size_t)size64;
2037             if ((((size + 1) >> kSecureDuplicateStepBits) & 1) != 0)
2038             {
2039               size -= (1 << kSecureDuplicateStepBits);
2040               SecurData.Alloc(size);
2041               if (ReadStream_FALSE(sdsStream, SecurData, size) == S_OK)
2042               {
2043                 ParseSecuritySDS();
2044                 break;
2045               }
2046             }
2047           }
2048         }
2049         break;
2050       }
2051     }
2052   }
2053 
2054   bool thereAreUnknownFolders_Normal = false;
2055   bool thereAreUnknownFolders_Deleted = false;
2056 
2057   for (i = 0; i < Items.Size(); i++)
2058   {
2059     CItem &item = Items[i];
2060     const CMftRec &rec = Recs[item.RecIndex];
2061     const CFileNameAttr &fn = rec.FileNames[item.NameIndex];
2062     const CMftRef &parentDirRef = fn.ParentDirRef;
2063     UInt64 refIndex = parentDirRef.GetIndex();
2064     if (refIndex == kRecIndex_RootDir)
2065       item.ParentFolder = -1;
2066     else
2067     {
2068       int index = FindDirItemForMtfRec(refIndex);
2069       if (index < 0 ||
2070           Recs[Items[index].RecIndex].SeqNumber != parentDirRef.GetNumber())
2071       {
2072         if (Recs[item.RecIndex].InUse())
2073         {
2074           thereAreUnknownFolders_Normal = true;
2075           index = k_ParentFolderIndex_Lost;
2076         }
2077         else
2078         {
2079           thereAreUnknownFolders_Deleted = true;
2080           index = k_ParentFolderIndex_Deleted;
2081         }
2082       }
2083       item.ParentFolder = index;
2084     }
2085   }
2086 
2087   unsigned virtIndex = Items.Size();
2088   if (_showSystemFiles)
2089   {
2090     _systemFolderIndex = virtIndex++;
2091     VirtFolderNames.Add(kVirtualFolder_System);
2092   }
2093   if (thereAreUnknownFolders_Normal)
2094   {
2095     _lostFolderIndex_Normal = virtIndex++;
2096     VirtFolderNames.Add(kVirtualFolder_Lost_Normal);
2097   }
2098   if (thereAreUnknownFolders_Deleted)
2099   {
2100     _lostFolderIndex_Deleted = virtIndex++;
2101     VirtFolderNames.Add(kVirtualFolder_Lost_Deleted);
2102   }
2103 
2104   return S_OK;
2105 }
2106 
2107 class CHandler:
2108   public IInArchive,
2109   public IArchiveGetRawProps,
2110   public IInArchiveGetStream,
2111   public ISetProperties,
2112   public CMyUnknownImp,
2113   CDatabase
2114 {
2115 public:
2116   MY_UNKNOWN_IMP4(
2117       IInArchive,
2118       IArchiveGetRawProps,
2119       IInArchiveGetStream,
2120       ISetProperties)
2121   INTERFACE_IInArchive(;)
2122   INTERFACE_IArchiveGetRawProps(;)
2123   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
2124   STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
2125 };
2126 
GetNumRawProps(UInt32 * numProps)2127 STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
2128 {
2129   *numProps = 2;
2130   return S_OK;
2131 }
2132 
GetRawPropInfo(UInt32 index,BSTR * name,PROPID * propID)2133 STDMETHODIMP CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)
2134 {
2135   *name = NULL;
2136   *propID = index == 0 ? kpidNtReparse : kpidNtSecure;
2137   return S_OK;
2138 }
2139 
GetParent(UInt32 index,UInt32 * parent,UInt32 * parentType)2140 STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)
2141 {
2142   *parentType = NParentType::kDir;
2143   int par = -1;
2144 
2145   if (index < Items.Size())
2146   {
2147     const CItem &item = Items[index];
2148 
2149     if (item.ParentHost >= 0)
2150     {
2151       *parentType = NParentType::kAltStream;
2152       par = (item.RecIndex == kRecIndex_RootDir ? -1 : item.ParentHost);
2153     }
2154     else if (item.RecIndex < kNumSysRecs)
2155     {
2156       if (_showSystemFiles)
2157         par = _systemFolderIndex;
2158     }
2159     else if (item.ParentFolder >= 0)
2160       par = item.ParentFolder;
2161     else if (item.ParentFolder == k_ParentFolderIndex_Lost)
2162       par = _lostFolderIndex_Normal;
2163     else if (item.ParentFolder == k_ParentFolderIndex_Deleted)
2164       par = _lostFolderIndex_Deleted;
2165   }
2166   *parent = (UInt32)(Int32)par;
2167   return S_OK;
2168 }
2169 
GetRawProp(UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)2170 STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
2171 {
2172   *data = NULL;
2173   *dataSize = 0;
2174   *propType = 0;
2175 
2176   if (propID == kpidName)
2177   {
2178     #ifdef MY_CPU_LE
2179     const UString2 *s;
2180     if (index >= Items.Size())
2181       s = &VirtFolderNames[index - Items.Size()];
2182     else
2183     {
2184       const CItem &item = Items[index];
2185       const CMftRec &rec = Recs[item.RecIndex];
2186       if (item.IsAltStream())
2187         s = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start].Name;
2188       else
2189         s = &rec.FileNames[item.NameIndex].Name;
2190     }
2191     if (s->IsEmpty())
2192       *data = (const wchar_t *)EmptyString;
2193     else
2194       *data = s->GetRawPtr();
2195     *dataSize = (s->Len() + 1) * (UInt32)sizeof(wchar_t);
2196     *propType = PROP_DATA_TYPE_wchar_t_PTR_Z_LE;
2197     #endif
2198     return S_OK;
2199   }
2200 
2201   if (propID == kpidNtReparse)
2202   {
2203     if (index >= Items.Size())
2204       return S_OK;
2205     const CItem &item = Items[index];
2206     const CMftRec &rec = Recs[item.RecIndex];
2207     const CByteBuffer &reparse = rec.ReparseData;
2208 
2209     if (reparse.Size() != 0)
2210     {
2211       *dataSize = (UInt32)reparse.Size();
2212       *propType = NPropDataType::kRaw;
2213       *data = (const Byte *)reparse;
2214     }
2215   }
2216 
2217   if (propID == kpidNtSecure)
2218   {
2219     if (index >= Items.Size())
2220       return S_OK;
2221     const CItem &item = Items[index];
2222     const CMftRec &rec = Recs[item.RecIndex];
2223     if (rec.SiAttr.SecurityId > 0)
2224     {
2225       UInt64 offset;
2226       UInt32 size;
2227       if (FindSecurityDescritor(rec.SiAttr.SecurityId, offset, size))
2228       {
2229         *dataSize = size;
2230         *propType = NPropDataType::kRaw;
2231         *data = (const Byte *)SecurData + offset;
2232       }
2233     }
2234   }
2235 
2236   return S_OK;
2237 }
2238 
GetStream(UInt32 index,ISequentialInStream ** stream)2239 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
2240 {
2241   COM_TRY_BEGIN
2242   *stream = 0;
2243   if (index >= Items.Size())
2244     return S_OK;
2245   IInStream *stream2;
2246   const CItem &item = Items[index];
2247   const CMftRec &rec = Recs[item.RecIndex];
2248   HRESULT res = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &stream2);
2249   *stream = (ISequentialInStream *)stream2;
2250   return res;
2251   COM_TRY_END
2252 }
2253 
2254 /*
2255 enum
2256 {
2257   kpidLink2 = kpidUserDefined,
2258   kpidLinkType,
2259   kpidRecMTime,
2260   kpidRecMTime2,
2261   kpidMTime2,
2262   kpidCTime2,
2263   kpidATime2
2264 };
2265 
2266 static const CStatProp kProps[] =
2267 {
2268   { NULL, kpidPath, VT_BSTR},
2269   { NULL, kpidSize, VT_UI8},
2270   { NULL, kpidPackSize, VT_UI8},
2271 
2272   // { NULL, kpidLink, VT_BSTR},
2273 
2274   // { "Link 2", kpidLink2, VT_BSTR},
2275   // { "Link Type", kpidLinkType, VT_UI2},
2276   { NULL, kpidINode, VT_UI8},
2277 
2278   { NULL, kpidMTime, VT_FILETIME},
2279   { NULL, kpidCTime, VT_FILETIME},
2280   { NULL, kpidATime, VT_FILETIME},
2281 
2282   // { "Record Modified", kpidRecMTime, VT_FILETIME},
2283 
2284   // { "Modified 2", kpidMTime2, VT_FILETIME},
2285   // { "Created 2", kpidCTime2, VT_FILETIME},
2286   // { "Accessed 2", kpidATime2, VT_FILETIME},
2287   // { "Record Modified 2", kpidRecMTime2, VT_FILETIME},
2288 
2289   { NULL, kpidAttrib, VT_UI4},
2290   { NULL, kpidNumBlocks, VT_UI4},
2291   { NULL, kpidIsDeleted, VT_BOOL},
2292 };
2293 */
2294 
2295 static const Byte kProps[] =
2296 {
2297   kpidPath,
2298   kpidIsDir,
2299   kpidSize,
2300   kpidPackSize,
2301   kpidMTime,
2302   kpidCTime,
2303   kpidATime,
2304   kpidAttrib,
2305   kpidLinks,
2306   kpidINode,
2307   kpidNumBlocks,
2308   kpidNumAltStreams,
2309   kpidIsAltStream,
2310   kpidShortName,
2311   kpidIsDeleted
2312 };
2313 
2314 enum
2315 {
2316   kpidRecordSize = kpidUserDefined
2317 };
2318 
2319 static const CStatProp kArcProps[] =
2320 {
2321   { NULL, kpidVolumeName, VT_BSTR},
2322   { NULL, kpidFileSystem, VT_BSTR},
2323   { NULL, kpidClusterSize, VT_UI4},
2324   { NULL, kpidSectorSize, VT_UI4},
2325   { "Record Size", kpidRecordSize, VT_UI4},
2326   { NULL, kpidHeadersSize, VT_UI8},
2327   { NULL, kpidCTime, VT_FILETIME},
2328   { NULL, kpidId, VT_UI8},
2329 };
2330 
2331 /*
2332 static const Byte kArcProps[] =
2333 {
2334   kpidVolumeName,
2335   kpidFileSystem,
2336   kpidClusterSize,
2337   kpidHeadersSize,
2338   kpidCTime,
2339 
2340   kpidSectorSize,
2341   kpidId
2342   // kpidSectorsPerTrack,
2343   // kpidNumHeads,
2344   // kpidHiddenSectors
2345 };
2346 */
2347 
2348 IMP_IInArchive_Props
2349 IMP_IInArchive_ArcProps_WITH_NAME
2350 
NtfsTimeToProp(UInt64 t,NCOM::CPropVariant & prop)2351 static void NtfsTimeToProp(UInt64 t, NCOM::CPropVariant &prop)
2352 {
2353   FILETIME ft;
2354   ft.dwLowDateTime = (DWORD)t;
2355   ft.dwHighDateTime = (DWORD)(t >> 32);
2356   prop = ft;
2357 }
2358 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)2359 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
2360 {
2361   COM_TRY_BEGIN
2362   NCOM::CPropVariant prop;
2363 
2364   const CMftRec *volRec = (Recs.Size() > kRecIndex_Volume ? &Recs[kRecIndex_Volume] : NULL);
2365 
2366   switch (propID)
2367   {
2368     case kpidClusterSize: prop = Header.ClusterSize(); break;
2369     case kpidPhySize: prop = PhySize; break;
2370     /*
2371     case kpidHeadersSize:
2372     {
2373       UInt64 val = 0;
2374       for (unsigned i = 0; i < kNumSysRecs; i++)
2375       {
2376         printf("\n%2d: %8I64d ", i, Recs[i].GetPackSize());
2377         if (i == 8)
2378           i = i
2379         val += Recs[i].GetPackSize();
2380       }
2381       prop = val;
2382       break;
2383     }
2384     */
2385     case kpidCTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.CTime, prop); break;
2386     case kpidMTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.MTime, prop); break;
2387     case kpidShortComment:
2388     case kpidVolumeName:
2389     {
2390       FOR_VECTOR (i, VolAttrs)
2391       {
2392         const CAttr &attr = VolAttrs[i];
2393         if (attr.Type == ATTR_TYPE_VOLUME_NAME)
2394         {
2395           UString2 name;
2396           GetString(attr.Data, (unsigned)attr.Data.Size() / 2, name);
2397           if (!name.IsEmpty())
2398             prop = name.GetRawPtr();
2399           break;
2400         }
2401       }
2402       break;
2403     }
2404     case kpidFileSystem:
2405     {
2406       AString s ("NTFS");
2407       FOR_VECTOR (i, VolAttrs)
2408       {
2409         const CAttr &attr = VolAttrs[i];
2410         if (attr.Type == ATTR_TYPE_VOLUME_INFO)
2411         {
2412           CVolInfo vi;
2413           if (attr.ParseVolInfo(vi))
2414           {
2415             s.Add_Space();
2416             s.Add_UInt32(vi.MajorVer);
2417             s += '.';
2418             s.Add_UInt32(vi.MinorVer);
2419           }
2420           break;
2421         }
2422       }
2423       prop = s;
2424       break;
2425     }
2426     case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
2427     case kpidRecordSize: prop = (UInt32)1 << RecSizeLog; break;
2428     case kpidId: prop = Header.SerialNumber; break;
2429 
2430     case kpidIsTree: prop = true; break;
2431     case kpidIsDeleted: prop = _showDeletedFiles; break;
2432     case kpidIsAltStream: prop = ThereAreAltStreams; break;
2433     case kpidIsAux: prop = true; break;
2434     case kpidINode: prop = true; break;
2435 
2436     case kpidWarning:
2437       if (_lostFolderIndex_Normal >= 0)
2438         prop = "There are lost files";
2439       break;
2440 
2441     /*
2442     case kpidWarningFlags:
2443     {
2444       UInt32 flags = 0;
2445       if (_headerWarning)
2446         flags |= k_ErrorFlags_HeadersError;
2447       if (flags != 0)
2448         prop = flags;
2449       break;
2450     }
2451     */
2452 
2453     // case kpidMediaType: prop = Header.MediaType; break;
2454     // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
2455     // case kpidNumHeads: prop = Header.NumHeads; break;
2456     // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
2457   }
2458   prop.Detach(value);
2459   return S_OK;
2460   COM_TRY_END
2461 }
2462 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)2463 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
2464 {
2465   COM_TRY_BEGIN
2466   NCOM::CPropVariant prop;
2467   if (index >= Items.Size())
2468   {
2469     switch (propID)
2470     {
2471       case kpidName:
2472       case kpidPath:
2473         prop = VirtFolderNames[index - Items.Size()].GetRawPtr();
2474         break;
2475       case kpidIsDir: prop = true; break;
2476       case kpidIsAux: prop = true; break;
2477       case kpidIsDeleted:
2478         if ((int)index == _lostFolderIndex_Deleted)
2479           prop = true;
2480         break;
2481     }
2482     prop.Detach(value);
2483     return S_OK;
2484   }
2485 
2486   const CItem &item = Items[index];
2487   const CMftRec &rec = Recs[item.RecIndex];
2488 
2489   const CAttr *data= NULL;
2490   if (item.DataIndex >= 0)
2491     data = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start];
2492 
2493   // const CFileNameAttr *fn = &rec.FileNames[item.NameIndex];
2494   /*
2495   if (rec.FileNames.Size() > 0)
2496     fn = &rec.FileNames[0];
2497   */
2498 
2499   switch (propID)
2500   {
2501     case kpidPath:
2502       GetItemPath(index, prop);
2503       break;
2504 
2505     /*
2506     case kpidLink:
2507       if (!rec.ReparseAttr.SubsName.IsEmpty())
2508       {
2509         prop = rec.ReparseAttr.SubsName;
2510       }
2511       break;
2512     case kpidLink2:
2513       if (!rec.ReparseAttr.PrintName.IsEmpty())
2514       {
2515         prop = rec.ReparseAttr.PrintName;
2516       }
2517       break;
2518 
2519     case kpidLinkType:
2520       if (rec.ReparseAttr.Tag != 0)
2521       {
2522         prop = (rec.ReparseAttr.Tag & 0xFFFF);
2523       }
2524       break;
2525     */
2526 
2527     case kpidINode:
2528     {
2529       // const CMftRec &rec = Recs[item.RecIndex];
2530       // prop = ((UInt64)rec.SeqNumber << 48) | item.RecIndex;
2531       prop = item.RecIndex;
2532       break;
2533     }
2534     case kpidStreamId:
2535     {
2536       if (item.DataIndex >= 0)
2537         prop = ((UInt64)item.RecIndex << 32) | (unsigned)item.DataIndex;
2538       break;
2539     }
2540 
2541     case kpidName:
2542     {
2543       const UString2 *s;
2544       if (item.IsAltStream())
2545         s = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start].Name;
2546       else
2547         s = &rec.FileNames[item.NameIndex].Name;
2548       if (s->IsEmpty())
2549         prop = (const wchar_t *)EmptyString;
2550       else
2551         prop = s->GetRawPtr();
2552       break;
2553     }
2554 
2555     case kpidShortName:
2556     {
2557       if (!item.IsAltStream())
2558       {
2559         int dosNameIndex = rec.FindDosName(item.NameIndex);
2560         if (dosNameIndex >= 0)
2561         {
2562           const UString2 &s = rec.FileNames[dosNameIndex].Name;
2563           if (s.IsEmpty())
2564             prop = (const wchar_t *)EmptyString;
2565           else
2566             prop = s.GetRawPtr();
2567         }
2568       }
2569       break;
2570     }
2571 
2572     case kpidIsDir: prop = item.IsDir(); break;
2573     case kpidIsAltStream: prop = item.IsAltStream(); break;
2574     case kpidIsDeleted: prop = !rec.InUse(); break;
2575     case kpidIsAux: prop = false; break;
2576 
2577     case kpidMTime: NtfsTimeToProp(rec.SiAttr.MTime, prop); break;
2578     case kpidCTime: NtfsTimeToProp(rec.SiAttr.CTime, prop); break;
2579     case kpidATime: NtfsTimeToProp(rec.SiAttr.ATime, prop); break;
2580     // case kpidRecMTime: if (fn) NtfsTimeToProp(rec.SiAttr.ThisRecMTime, prop); break;
2581 
2582     /*
2583     case kpidMTime2: if (fn) NtfsTimeToProp(fn->MTime, prop); break;
2584     case kpidCTime2: if (fn) NtfsTimeToProp(fn->CTime, prop); break;
2585     case kpidATime2: if (fn) NtfsTimeToProp(fn->ATime, prop); break;
2586     case kpidRecMTime2: if (fn) NtfsTimeToProp(fn->ThisRecMTime, prop); break;
2587     */
2588 
2589     case kpidAttrib:
2590     {
2591       UInt32 attrib;
2592       /* WinXP-64: The CFileNameAttr::Attrib is not updated  after some changes. Why?
2593          CSiAttr:attrib is updated better. So we use CSiAttr:Sttrib */
2594       /*
2595       if (fn)
2596         attrib = fn->Attrib;
2597       else
2598       */
2599         attrib = rec.SiAttr.Attrib;
2600       if (item.IsDir())
2601         attrib |= FILE_ATTRIBUTE_DIRECTORY;
2602 
2603       /* some system entries can contain extra flags (Index View).
2604       // 0x10000000   (Directory)
2605       // 0x20000000   FILE_ATTR_VIEW_INDEX_PRESENT MFT_RECORD_IS_VIEW_INDEX (Index View)
2606       But we don't need them */
2607       attrib &= 0xFFFF;
2608 
2609       prop = attrib;
2610       break;
2611     }
2612     case kpidLinks: if (rec.MyNumNameLinks != 1) prop = rec.MyNumNameLinks; break;
2613 
2614     case kpidNumAltStreams:
2615     {
2616       if (!item.IsAltStream())
2617       {
2618         unsigned num = rec.DataRefs.Size();
2619         if (num > 0)
2620         {
2621           if (!rec.IsDir() && rec.DataAttrs[rec.DataRefs[0].Start].Name.IsEmpty())
2622             num--;
2623           if (num > 0)
2624             prop = num;
2625         }
2626       }
2627       break;
2628     }
2629 
2630     case kpidSize: if (data) prop = data->GetSize(); else if (!item.IsDir()) prop = (UInt64)0; break;
2631     case kpidPackSize: if (data) prop = data->GetPackSize(); else if (!item.IsDir()) prop = (UInt64)0; break;
2632     case kpidNumBlocks: if (data) prop = (UInt32)rec.GetNumExtents(item.DataIndex, Header.ClusterSizeLog, Header.NumClusters); break;
2633   }
2634   prop.Detach(value);
2635   return S_OK;
2636   COM_TRY_END
2637 }
2638 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback)2639 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
2640 {
2641   COM_TRY_BEGIN
2642   {
2643     OpenCallback = callback;
2644     InStream = stream;
2645     HRESULT res;
2646     try
2647     {
2648       res = CDatabase::Open();
2649       if (res == S_OK)
2650         return S_OK;
2651     }
2652     catch(...)
2653     {
2654       Close();
2655       throw;
2656     }
2657     Close();
2658     return res;
2659   }
2660   COM_TRY_END
2661 }
2662 
Close()2663 STDMETHODIMP CHandler::Close()
2664 {
2665   ClearAndClose();
2666   return S_OK;
2667 }
2668 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)2669 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2670     Int32 testMode, IArchiveExtractCallback *extractCallback)
2671 {
2672   COM_TRY_BEGIN
2673   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2674   if (allFilesMode)
2675     numItems = Items.Size();
2676   if (numItems == 0)
2677     return S_OK;
2678   UInt32 i;
2679   UInt64 totalSize = 0;
2680   for (i = 0; i < numItems; i++)
2681   {
2682     UInt32 index = allFilesMode ? i : indices[i];
2683     if (index >= (UInt32)Items.Size())
2684       continue;
2685     const CItem &item = Items[allFilesMode ? i : indices[i]];
2686     const CMftRec &rec = Recs[item.RecIndex];
2687     if (item.DataIndex >= 0)
2688       totalSize += rec.GetSize(item.DataIndex);
2689   }
2690   RINOK(extractCallback->SetTotal(totalSize));
2691 
2692   UInt64 totalPackSize;
2693   totalSize = totalPackSize = 0;
2694 
2695   UInt32 clusterSize = Header.ClusterSize();
2696   CByteBuffer buf(clusterSize);
2697 
2698   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
2699   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
2700 
2701   CLocalProgress *lps = new CLocalProgress;
2702   CMyComPtr<ICompressProgressInfo> progress = lps;
2703   lps->Init(extractCallback, false);
2704 
2705   CDummyOutStream *outStreamSpec = new CDummyOutStream;
2706   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
2707 
2708   for (i = 0; i < numItems; i++)
2709   {
2710     lps->InSize = totalPackSize;
2711     lps->OutSize = totalSize;
2712     RINOK(lps->SetCur());
2713     CMyComPtr<ISequentialOutStream> realOutStream;
2714     Int32 askMode = testMode ?
2715         NExtract::NAskMode::kTest :
2716         NExtract::NAskMode::kExtract;
2717     UInt32 index = allFilesMode ? i : indices[i];
2718     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
2719 
2720     if (index >= (UInt32)Items.Size() || Items[index].IsDir())
2721     {
2722       RINOK(extractCallback->PrepareOperation(askMode));
2723       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
2724       continue;
2725     }
2726 
2727     const CItem &item = Items[index];
2728 
2729     if (!testMode && !realOutStream)
2730       continue;
2731     RINOK(extractCallback->PrepareOperation(askMode));
2732 
2733     outStreamSpec->SetStream(realOutStream);
2734     realOutStream.Release();
2735     outStreamSpec->Init();
2736 
2737     const CMftRec &rec = Recs[item.RecIndex];
2738 
2739     int res = NExtract::NOperationResult::kDataError;
2740     {
2741       CMyComPtr<IInStream> inStream;
2742       HRESULT hres = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &inStream);
2743       if (hres == S_FALSE)
2744         res = NExtract::NOperationResult::kUnsupportedMethod;
2745       else
2746       {
2747         RINOK(hres);
2748         if (inStream)
2749         {
2750           hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
2751           if (hres != S_OK &&  hres != S_FALSE)
2752           {
2753             RINOK(hres);
2754           }
2755           if (/* copyCoderSpec->TotalSize == item.GetSize() && */ hres == S_OK)
2756             res = NExtract::NOperationResult::kOK;
2757         }
2758       }
2759     }
2760     if (item.DataIndex >= 0)
2761     {
2762       const CAttr &data = rec.DataAttrs[rec.DataRefs[item.DataIndex].Start];
2763       totalPackSize += data.GetPackSize();
2764       totalSize += data.GetSize();
2765     }
2766     outStreamSpec->ReleaseStream();
2767     RINOK(extractCallback->SetOperationResult(res));
2768   }
2769   return S_OK;
2770   COM_TRY_END
2771 }
2772 
GetNumberOfItems(UInt32 * numItems)2773 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
2774 {
2775   *numItems = Items.Size() + VirtFolderNames.Size();
2776   return S_OK;
2777 }
2778 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)2779 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
2780 {
2781   InitProps();
2782 
2783   for (UInt32 i = 0; i < numProps; i++)
2784   {
2785     const wchar_t *name = names[i];
2786     const PROPVARIANT &prop = values[i];
2787 
2788     if (StringsAreEqualNoCase_Ascii(name, "ld"))
2789     {
2790       RINOK(PROPVARIANT_to_bool(prop, _showDeletedFiles));
2791     }
2792     else if (StringsAreEqualNoCase_Ascii(name, "ls"))
2793     {
2794       RINOK(PROPVARIANT_to_bool(prop, _showSystemFiles));
2795     }
2796     else
2797       return E_INVALIDARG;
2798   }
2799   return S_OK;
2800 }
2801 
2802 static const Byte k_Signature[] = { 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' ', 0 };
2803 
2804 REGISTER_ARC_I(
2805   "NTFS", "ntfs img", 0, 0xD9,
2806   k_Signature,
2807   3,
2808   0,
2809   NULL)
2810 
2811 }}
2812