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