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