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