VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/ntfsvfs.cpp@ 69924

Last change on this file since 69924 was 69924, checked in by vboxsync, 7 years ago

iprt/ntfsvfs.cpp: working on the directory access (readonly) bits

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 190.1 KB
Line 
1/* $Id: ntfsvfs.cpp 69924 2017-12-04 21:12:01Z vboxsync $ */
2/** @file
3 * IPRT - NTFS Virtual Filesystem, currently only for reading allocation bitmap.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include <iprt/fsvfs.h>
33
34#include <iprt/asm.h>
35#include <iprt/avl.h>
36#include <iprt/assert.h>
37#include <iprt/file.h>
38#include <iprt/log.h>
39#include <iprt/mem.h>
40#include <iprt/string.h>
41#include <iprt/vfs.h>
42#include <iprt/vfslowlevel.h>
43#include <iprt/utf16.h>
44#include <iprt/formats/ntfs.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** The maximum bitmap size to try cache in its entirity (in bytes). */
51#define RTFSNTFS_MAX_WHOLE_BITMAP_CACHE _64K
52/** The maximum node cache size (in bytes). */
53#if ARCH_BITS >= 64
54# define RTFSNTFS_MAX_NODE_CACHE_SIZE _1M
55#else
56# define RTFSNTFS_MAX_NODE_CACHE_SIZE _256K
57#endif
58
59/** Makes a combined NTFS version value.
60 * @see RTFSNTFSVOL::uNtfsVersion */
61#define RTFSNTFS_MAKE_VERSION(a_uMajor, a_uMinor) RT_MAKE_U16(a_uMinor, a_uMajor)
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67/** Pointer to the instance data for a NTFS volume. */
68typedef struct RTFSNTFSVOL *PRTFSNTFSVOL;
69/** Pointer to a NTFS MFT record. */
70typedef struct RTFSNTFSMFTREC *PRTFSNTFSMFTREC;
71/** Poitner to a NTFS core object record. */
72typedef struct RTFSNTFSCORE *PRTFSNTFSCORE;
73/** Pointer to an index node. */
74typedef struct RTFSNTFSIDXNODE *PRTFSNTFSIDXNODE;
75/** Pointer to a shared NTFS directory object. */
76typedef struct RTFSNTFSDIRSHRD *PRTFSNTFSDIRSHRD;
77/** Pointer to a shared NTFS file object. */
78typedef struct RTFSNTFSFILESHRD *PRTFSNTFSFILESHRD;
79
80
81/**
82 * NTFS disk allocation extent (internal representation).
83 */
84typedef struct RTFSNTFSEXTENT
85{
86 /** The disk or partition byte offset.
87 * This is set to UINT64_MAX for parts of sparse files that aren't recorded. */
88 uint64_t off;
89 /** The size of the extent in bytes. */
90 uint64_t cbExtent;
91} RTFSNTFSEXTENT;
92/** Pointer to an NTFS 9660 extent. */
93typedef RTFSNTFSEXTENT *PRTFSNTFSEXTENT;
94/** Pointer to a const NTFS 9660 extent. */
95typedef RTFSNTFSEXTENT const *PCRTFSNTFSEXTENT;
96
97/**
98 * An array of zero or more extents.
99 */
100typedef struct RTFSNTFSEXTENTS
101{
102 /** Number of bytes covered by the extents. */
103 uint64_t cbData;
104 /** Number of allocation extents. */
105 uint32_t cExtents;
106 /** Array of allocation extents. */
107 PRTFSNTFSEXTENT paExtents;
108} RTFSNTFSEXTENTS;
109/** Pointer to an extent array. */
110typedef RTFSNTFSEXTENTS *PRTFSNTFSEXTENTS;
111/** Pointer to a const extent array. */
112typedef RTFSNTFSEXTENTS const *PCRTFSNTFSEXTENTS;
113
114
115/**
116 * NTFS MFT record.
117 *
118 * These are kept in a tree to , so
119 */
120typedef struct RTFSNTFSMFTREC
121{
122 /** MFT record number (index) as key. */
123 AVLU64NODECORE TreeNode;
124 /** Pointer to the next MFT record if chained. Holds a reference. */
125 PRTFSNTFSMFTREC pNext;
126 union
127 {
128 /** Generic record pointer. RTFSNTFSVOL::cbMftRecord in size. */
129 uint8_t *pbRec;
130 /** Pointer to the file record. */
131 PNTFSRECFILE pFileRec;
132 } RT_UNION_NM(u);
133 /** Pointer to the core object with the parsed data.
134 * This is a weak reference. Non-base MFT record all point to the base one. */
135 PRTFSNTFSCORE pCore;
136 /** Reference counter. */
137 uint32_t volatile cRefs;
138 /** Set if this is a base MFT record. */
139 bool fIsBase;
140} RTFSNTFSMFTREC;
141
142
143/** Pointer to a attribute subrecord structure. */
144typedef struct RTFSNTFSATTRSUBREC *PRTFSNTFSATTRSUBREC;
145
146/**
147 * An attribute subrecord.
148 *
149 * This is for covering non-resident attributes that have had their allocation
150 * list split.
151 */
152typedef struct RTFSNTFSATTRSUBREC
153{
154 /** Pointer to the next one. */
155 PRTFSNTFSATTRSUBREC pNext;
156 /** Pointer to the attribute header.
157 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
158 PNTFSATTRIBHDR pAttrHdr;
159 /** Disk space allocation if non-resident. */
160 RTFSNTFSEXTENTS Extents;
161} RTFSNTFSATTRSUBREC;
162
163/**
164 * An attribute.
165 */
166typedef struct RTFSNTFSATTR
167{
168 /** List entry (head RTFSNTFSCORE::AttribHead). */
169 RTLISTNODE ListEntry;
170 /** Pointer to the core object this attribute belongs to. */
171 PRTFSNTFSCORE pCore;
172 /** Pointer to the attribute header.
173 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
174 PNTFSATTRIBHDR pAttrHdr;
175 /** The offset of the attribute header in the MFT record.
176 * This is needed to validate header relative offsets. */
177 uint32_t offAttrHdrInMftRec;
178 /** Number of resident bytes available (can be smaller than cbValue).
179 * Set to zero for non-resident attributes. */
180 uint32_t cbResident;
181 /** The (uncompressed) attribute size. */
182 uint64_t cbValue;
183 /** Disk space allocation if non-resident. */
184 RTFSNTFSEXTENTS Extents;
185 /** Pointer to any subrecords containing further allocation extents. */
186 PRTFSNTFSATTRSUBREC pSubRecHead;
187 /** Pointer to the VFS object for this attribute.
188 * This is a weak reference since it's the VFS object that is referencing us. */
189 union
190 {
191 /** Pointer to a shared directory (NTFS_AT_DIRECTORY). */
192 PRTFSNTFSDIRSHRD pSharedDir;
193 /** Pointer to a shared file (NTFS_AT_DATA). */
194 PRTFSNTFSFILESHRD pSharedFile;
195 } uObj;
196} RTFSNTFSATTR;
197/** Pointer to a attribute structure. */
198typedef RTFSNTFSATTR *PRTFSNTFSATTR;
199
200
201/**
202 * NTFS file system object, shared part.
203 */
204typedef struct RTFSNTFSCORE
205{
206 /** Reference counter. */
207 uint32_t volatile cRefs;
208 /** Pointer to the volume. */
209 PRTFSNTFSVOL pVol;
210 /** Pointer to the head of the MFT record chain for this object.
211 * Holds a reference. */
212 PRTFSNTFSMFTREC pMftRec;
213 /** List of attributes (RTFSNTFSATTR). */
214 RTLISTANCHOR AttribHead;
215} RTFSNTFSCORE;
216
217
218/**
219 * Node lookup information for facilitating binary searching of node.
220 */
221typedef struct RTFSNTFSIDXNODEINFO
222{
223 /** The index header. */
224 PCNTFSINDEXHDR pIndexHdr;
225 /** Number of entries. */
226 uint32_t cEntries;
227 /** Set if internal node. */
228 bool fInternal;
229 /** Array with pointers to the entries. */
230 PCNTFSIDXENTRYHDR *papEntries;
231 /** Pointer to the index node this info is for, NULL if root node.
232 * This is for reducing the enumeration stack entry size. */
233 PRTFSNTFSIDXNODE pNode;
234 /** Pointer to the NTFS volume instace. */
235 PRTFSNTFSVOL pVol;
236} RTFSNTFSIDXNODEINFO;
237/** Pointer to index node lookup info. */
238typedef RTFSNTFSIDXNODEINFO *PRTFSNTFSIDXNODEINFO;
239/** Pointer to const index node lookup info. */
240typedef RTFSNTFSIDXNODEINFO const *PCRTFSNTFSIDXNODEINFO;
241
242/**
243 * Index node, cached.
244 *
245 * These are cached to avoid reading, validating and parsing things each time a
246 * subnode is accessed.
247 */
248typedef struct RTFSNTFSIDXNODE
249{
250 /** Entry in RTFSNTFSVOL::IdxNodeCahceRoot, key is disk byte offset. */
251 AVLU64NODECORE TreeNode;
252 /** List entry on the unused list. Gets removed from it when cRefs is
253 * increase to one, and added when it reaches zero. */
254 RTLISTNODE UnusedListEntry;
255 /** Reference counter. */
256 uint32_t volatile cRefs;
257 /** The estimated memory cost of this node. */
258 uint32_t cbCost;
259 /** Pointer to the node data. */
260 PNTFSATINDEXALLOC pNode;
261 /** Node info. */
262 RTFSNTFSIDXNODEINFO NodeInfo;
263} RTFSNTFSIDXNODE;
264
265/**
266 * Common index root structure.
267 */
268typedef struct RTFSNTFSIDXROOTINFO
269{
270 /** Pointer to the index root attribute value. */
271 PCNTFSATINDEXROOT pRoot;
272 /** Pointer to the index allocation attribute, if present.
273 * This and the bitmap may be absent if the whole directory fits into the
274 * root index. */
275 PRTFSNTFSATTR pAlloc;
276 /** End of the node addresses range (exclusive). */
277 uint64_t uEndNodeAddresses;
278 /** Node address misalignement mask. */
279 uint32_t fNodeAddressMisalign;
280 /** The byte shift count for node addresses. */
281 uint8_t cNodeAddressByteShift;
282 /** Node info for the root. */
283 RTFSNTFSIDXNODEINFO NodeInfo;
284 /** Pointer to the index root attribute. We reference the core thru this and
285 * use it to zero RTFSNTFSATTR::uObj::pSharedDir on destruction. */
286 PRTFSNTFSATTR pRootAttr;
287} RTFSNTFSIDXROOTINFO;
288/** Pointer to an index root structure. */
289typedef RTFSNTFSIDXROOTINFO *PRTFSNTFSIDXROOTINFO;
290/** Pointer to a const index root structure. */
291typedef RTFSNTFSIDXROOTINFO const *PCRTFSNTFSIDXROOTINFO;
292
293/**
294 * Pointer to a shared NTFS directory object.
295 */
296typedef struct RTFSNTFSDIRSHRD
297{
298 /** Reference counter. */
299 uint32_t volatile cRefs;
300 /** Index root information. */
301 RTFSNTFSIDXROOTINFO RootInfo;
302} RTFSNTFSDIRSHRD;
303
304/**
305 * Index stack entry for index enumeration.
306 */
307typedef struct RTFSNTFSIDXSTACKENTRY
308{
309 /** The next entry to process in this stack entry. */
310 uint32_t iNext;
311 /** Pointer to the node info for this entry. */
312 PRTFSNTFSIDXNODEINFO pNodeInfo;
313} RTFSNTFSIDXSTACKENTRY;
314/** Pointer to an index enumeration stack entry. */
315typedef RTFSNTFSIDXSTACKENTRY *PRTFSNTFSIDXSTACKENTRY;
316
317
318/**
319 * Open directory instance.
320 */
321typedef struct RTFSNTFSDIR
322{
323 /** Pointer to the shared directory instance (referenced). */
324 PRTFSNTFSDIRSHRD pShared;
325 /** Set if we've reached the end of the directory enumeration. */
326 bool fNoMoreFiles;
327 /** The enumeration stack size. */
328 uint32_t cEnumStackEntries;
329 /** The allocated enumeration stack depth. */
330 uint32_t cEnumStackMaxDepth;
331 /** The numeration stack. Allocated as needed. */
332 PRTFSNTFSIDXSTACKENTRY paEnumStack;
333} RTFSNTFSDIR;
334/** Pointer to an open directory instance. */
335typedef RTFSNTFSDIR *PRTFSNTFSDIR;
336
337
338/**
339 * Instance data for an NTFS volume.
340 */
341typedef struct RTFSNTFSVOL
342{
343 /** Handle to itself. */
344 RTVFS hVfsSelf;
345 /** The file, partition, or whatever backing the NTFS volume. */
346 RTVFSFILE hVfsBacking;
347 /** The size of the backing thingy. */
348 uint64_t cbBacking;
349 /** The formatted size of the volume. */
350 uint64_t cbVolume;
351 /** cbVolume expressed as a cluster count. */
352 uint64_t cClusters;
353
354 /** RTVFSMNT_F_XXX. */
355 uint32_t fMntFlags;
356 /** RTFSNTVFS_F_XXX (currently none defined). */
357 uint32_t fNtfsFlags;
358
359 /** The (logical) sector size. */
360 uint32_t cbSector;
361
362 /** The (logical) cluster size. */
363 uint32_t cbCluster;
364 /** Max cluster count value that won't overflow a signed 64-bit when
365 * converted to bytes. Inclusive. */
366 uint64_t iMaxVirtualCluster;
367 /** The shift count for converting between bytes and clusters. */
368 uint8_t cClusterShift;
369
370 /** Explicit padding. */
371 uint8_t abReserved[3];
372 /** The NTFS version of the volume (RTFSNTFS_MAKE_VERSION). */
373 uint16_t uNtfsVersion;
374 /** The NTFS_VOLUME_F_XXX. */
375 uint16_t fVolumeFlags;
376
377 /** The logical cluster number of the MFT. */
378 uint64_t uLcnMft;
379 /** The logical cluster number of the mirror MFT. */
380 uint64_t uLcnMftMirror;
381
382 /** The MFT record size. */
383 uint32_t cbMftRecord;
384 /** The default index (B-tree) node size. */
385 uint32_t cbDefaultIndexNode;
386
387 /** The volume serial number. */
388 uint64_t uSerialNo;
389
390 /** The '$Mft' data attribute. */
391 PRTFSNTFSATTR pMftData;
392
393 /** @name Allocation bitmap and cache.
394 * @{ */
395 /** The '$Bitmap' data attribute. */
396 PRTFSNTFSATTR pMftBitmap;
397 /** The first cluster currently loaded into the bitmap cache . */
398 uint64_t iFirstBitmapCluster;
399 /** The number of clusters currently loaded into the bitmap cache */
400 uint32_t cBitmapClusters;
401 /** The size of the pvBitmap allocation. */
402 uint32_t cbBitmapAlloc;
403 /** Allocation bitmap cache buffer. */
404 void *pvBitmap;
405 /** @} */
406
407 /** Root of the MFT record tree (RTFSNTFSMFTREC). */
408 AVLU64TREE MftRoot;
409
410 /** @name Directory/index related.
411 * @{ */
412 /** Tree of index nodes, index by disk byte offset. (RTFSNTFSIDXNODE) */
413 AVLU64TREE IdxNodeCacheRoot;
414 /** List of currently unreferenced index nodes. (RTFSNTFSIDXNODE)
415 * Most recently used nodes are found at the end of the list. Nodes are added
416 * when their reference counter reaches zero. They are removed when it
417 * increases to one again.
418 *
419 * The nodes are still in the index node cache tree (IdxNodeCacheRoot), but
420 * we'll trim this from the end when we reach a certain size. */
421 RTLISTANCHOR IdxNodeUnusedHead;
422 /** Number of unreferenced index nodes. */
423 uint32_t cUnusedIdxNodes;
424 /** Number of cached index nodes. */
425 uint32_t cIdxNodes;
426 /** Total index node memory cost. */
427 size_t cbIdxNodes;
428 /** The root directory. */
429 PRTFSNTFSDIRSHRD pRootDir;
430 /** Lower to uppercase conversion table for this filesystem.
431 * This always has 64K valid entries. */
432 PRTUTF16 pawcUpcase;
433 /** @} */
434
435} RTFSNTFSVOL;
436
437
438
439/*********************************************************************************************************************************
440* Internal Functions *
441*********************************************************************************************************************************/
442static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis);
443static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis);
444#ifdef LOG_ENABLED
445static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot);
446#endif
447static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir);
448static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis);
449static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode);
450static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode);
451
452
453
454/**
455 * Checks if a bit is set in an NTFS bitmap (little endian).
456 *
457 * @returns true if set, false if not.
458 * @param pvBitmap The bitmap buffer.
459 * @param iBit The bit.
460 */
461DECLINLINE(bool) rtFsNtfsBitmap_IsSet(void *pvBitmap, uint32_t iBit)
462{
463#if 0 //def RT_LITTLE_ENDIAN
464 return ASMBitTest(pvBitmap, iBit);
465#else
466 uint8_t b = ((uint8_t const *)pvBitmap)[iBit >> 3];
467 return RT_BOOL(b & (1 << (iBit & 7)));
468#endif
469}
470
471
472
473static PRTFSNTFSMFTREC rtFsNtfsVol_NewMftRec(PRTFSNTFSVOL pVol, uint64_t idMft)
474{
475 PRTFSNTFSMFTREC pRec = (PRTFSNTFSMFTREC)RTMemAllocZ(sizeof(*pRec));
476 if (pRec)
477 {
478 pRec->pbRec = (uint8_t *)RTMemAllocZ(pVol->cbMftRecord);
479 if (pRec->pbRec)
480 {
481 pRec->TreeNode.Key = idMft;
482 pRec->pNext = NULL;
483 pRec->cRefs = 1;
484 if (RTAvlU64Insert(&pVol->MftRoot, &pRec->TreeNode))
485 return pRec;
486 RTMemFree(pRec);
487 }
488 }
489 return NULL;
490}
491
492
493static uint32_t rtFsNtfsMftRec_Destroy(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
494{
495 RTMemFree(pThis->pbRec);
496 pThis->pbRec = NULL;
497
498 PAVLU64NODECORE pRemoved = RTAvlU64Remove(&pVol->MftRoot, pThis->TreeNode.Key);
499 Assert(pRemoved == &pThis->TreeNode); NOREF(pRemoved);
500
501 RTMemFree(pThis);
502
503 return 0;
504}
505
506
507static uint32_t rtFsNtfsMftRec_Retain(PRTFSNTFSMFTREC pThis)
508{
509 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
510 Assert(cRefs < 64);
511 return cRefs;
512}
513
514static uint32_t rtFsNtfsMftRec_Release(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
515{
516 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
517 Assert(cRefs < 64);
518 if (cRefs != 0)
519 return cRefs;
520 return rtFsNtfsMftRec_Destroy(pThis, pVol);
521}
522
523
524#ifdef LOG_ENABLED
525/**
526 * Logs the MFT record
527 *
528 * @param pRec The MFT record to log.
529 * @param cbMftRecord MFT record size (from RTFSNTFSVOL).
530 */
531static void rtfsNtfsMftRec_Log(PRTFSNTFSMFTREC pRec, uint32_t cbMftRecord)
532{
533 if (LogIs2Enabled())
534 {
535 PCNTFSRECFILE pFileRec = pRec->pFileRec;
536 Log2(("NTFS: MFT #%#RX64\n", pRec->TreeNode.Key));
537 if (pFileRec->Hdr.uMagic == NTFSREC_MAGIC_FILE)
538 {
539 size_t const cbRec = cbMftRecord;
540 uint8_t const * const pbRec = pRec->pbRec;
541
542 Log2(("NTFS: FILE record: \n"));
543 Log2(("NTFS: UpdateSeqArray %#x L %#x\n", RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray), RT_LE2H_U16(pFileRec->Hdr.cUpdateSeqEntries) ));
544 Log2(("NTFS: uLsn %#RX64\n", RT_LE2H_U64(pFileRec->uLsn)));
545 Log2(("NTFS: uRecReuseSeqNo %#RX16\n", RT_LE2H_U16(pFileRec->uRecReuseSeqNo)));
546 Log2(("NTFS: cLinks %#RX16\n", RT_LE2H_U16(pFileRec->cLinks)));
547 Log2(("NTFS: offFirstAttrib %#RX16\n", RT_LE2H_U16(pFileRec->offFirstAttrib)));
548 Log2(("NTFS: fFlags %#RX16%s%s\n", RT_LE2H_U16(pFileRec->fFlags),
549 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_IN_USE ? " in-use" : "",
550 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_DIRECTORY ? " directory" : ""));
551 Log2(("NTFS: cbRecUsed %#RX32\n", RT_LE2H_U32(pFileRec->cbRecUsed)));
552 Log2(("NTFS: BaseMftRec %#RX64, sqn %#x\n",
553 NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec), NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec)));
554 Log2(("NTFS: idNextAttrib %#RX16\n", RT_LE2H_U16(pFileRec->idNextAttrib)));
555 if ( RT_LE2H_U16(pFileRec->offFirstAttrib) >= sizeof(*pFileRec)
556 && ( RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray) >= sizeof(*pFileRec)
557 || pFileRec->Hdr.offUpdateSeqArray == 0))
558 {
559 Log2(("NTFS: uPaddingOrUsa %#RX16\n", pFileRec->uPaddingOrUsa));
560 Log2(("NTFS: idxMftSelf %#RX32\n", RT_LE2H_U32(pFileRec->idxMftSelf)));
561 }
562
563 uint32_t offRec = pFileRec->offFirstAttrib;
564 size_t cbRecUsed = RT_MIN(cbRec, pFileRec->cbRecUsed);
565 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
566 {
567 PCNTFSATTRIBHDR pHdr = (PCNTFSATTRIBHDR)&pbRec[offRec];
568 uint32_t const cbAttrib = RT_LE2H_U32(pHdr->cbAttrib);
569 Log2(("NTFS: @%#05x: Attrib record: %#x LB %#x, instance #%#x, fFlags=%#RX16, %s\n", offRec,
570 RT_LE2H_U32(pHdr->uAttrType), cbAttrib, RT_LE2H_U16(pHdr->idAttrib), RT_LE2H_U16(pHdr->fFlags),
571 pHdr->fNonResident == 0 ? "resident" : pHdr->fNonResident == 1 ? "non-resident" : "bad-resident-flag"));
572 if (pHdr->offName && pHdr->cwcName)
573 {
574 if (offRec + RT_LE2H_U16(pHdr->offName) + pHdr->cwcName * sizeof(RTUTF16) <= cbRec)
575 Log2(("NTFS: Name %.*ls\n", pHdr->cwcName,&pbRec[offRec + RT_LE2H_U16(pHdr->offName)]));
576 else
577 Log2(("NTFS: Name <!out of bounds!> %#x L %#x\n", RT_LE2H_U16(pHdr->offName), pHdr->cwcName));
578 }
579 switch (pHdr->uAttrType)
580 {
581 case NTFS_AT_UNUSED: Log2(("NTFS: Type: UNUSED\n")); break;
582 case NTFS_AT_STANDARD_INFORMATION: Log2(("NTFS: Type: STANDARD_INFORMATION\n")); break;
583 case NTFS_AT_ATTRIBUTE_LIST: Log2(("NTFS: Type: ATTRIBUTE_LIST\n")); break;
584 case NTFS_AT_FILENAME: Log2(("NTFS: Type: FILENAME\n")); break;
585 case NTFS_AT_OBJECT_ID: Log2(("NTFS: Type: OBJECT_ID\n")); break;
586 case NTFS_AT_SECURITY_DESCRIPTOR: Log2(("NTFS: Type: SECURITY_DESCRIPTOR\n")); break;
587 case NTFS_AT_VOLUME_NAME: Log2(("NTFS: Type: VOLUME_NAME\n")); break;
588 case NTFS_AT_VOLUME_INFORMATION: Log2(("NTFS: Type: VOLUME_INFORMATION\n")); break;
589 case NTFS_AT_DATA: Log2(("NTFS: Type: DATA\n")); break;
590 case NTFS_AT_INDEX_ROOT: Log2(("NTFS: Type: INDEX_ROOT\n")); break;
591 case NTFS_AT_INDEX_ALLOCATION: Log2(("NTFS: Type: INDEX_ALLOCATION\n")); break;
592 case NTFS_AT_BITMAP: Log2(("NTFS: Type: BITMAP\n")); break;
593 case NTFS_AT_REPARSE_POINT: Log2(("NTFS: Type: REPARSE_POINT\n")); break;
594 case NTFS_AT_EA_INFORMATION: Log2(("NTFS: Type: EA_INFORMATION\n")); break;
595 case NTFS_AT_EA: Log2(("NTFS: Type: EA\n")); break;
596 case NTFS_AT_PROPERTY_SET: Log2(("NTFS: Type: PROPERTY_SET\n")); break;
597 case NTFS_AT_LOGGED_UTILITY_STREAM: Log2(("NTFS: Type: LOGGED_UTILITY_STREAM\n")); break;
598 default:
599 if (RT_LE2H_U32(pHdr->uAttrType) >= RT_LE2H_U32_C(NTFS_AT_FIRST_USER_DEFINED))
600 Log2(("NTFS: Type: unknown user defined - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
601 else
602 Log2(("NTFS: Type: unknown - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
603 break;
604 }
605
606 size_t const cbMaxAttrib = cbRec - offRec;
607 if (!pHdr->fNonResident)
608 {
609 uint16_t const offValue = RT_LE2H_U16(pHdr->u.Res.offValue);
610 uint32_t const cbValue = RT_LE2H_U32(pHdr->u.Res.cbValue);
611 Log2(("NTFS: Value: %#x LB %#x, fFlags=%#x bReserved=%#x\n",
612 offValue, cbValue, pHdr->u.Res.fFlags, pHdr->u.Res.bReserved));
613 if ( offValue < cbMaxAttrib
614 && cbValue < cbMaxAttrib
615 && offValue + cbValue <= cbMaxAttrib)
616 {
617 uint8_t const *pbValue = &pbRec[offRec + offValue];
618 RTTIMESPEC Spec;
619 char sz[80];
620 switch (pHdr->uAttrType)
621 {
622 case NTFS_AT_STANDARD_INFORMATION:
623 {
624 PCNTFSATSTDINFO pInfo = (PCNTFSATSTDINFO)pbValue;
625 if (cbValue >= NTFSATSTDINFO_SIZE_NTFS_V12)
626 {
627 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
628 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
629 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
630 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
631 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
632 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
633 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
634 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
635 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
636 Log2(("NTFS: cMaxFileVersions %#RX32\n", RT_LE2H_U32(pInfo->cMaxFileVersions) ));
637 Log2(("NTFS: uFileVersion %#RX32\n", RT_LE2H_U32(pInfo->uFileVersion) ));
638 }
639 else
640 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATSTDINFO!\n",
641 cbValue, NTFSATSTDINFO_SIZE_NTFS_V12));
642 if (cbValue >= sizeof(*pInfo))
643 {
644 Log2(("NTFS: idClass %#RX32\n", RT_LE2H_U32(pInfo->idClass) ));
645 Log2(("NTFS: idOwner %#RX32\n", RT_LE2H_U32(pInfo->idOwner) ));
646 Log2(("NTFS: idSecurity %#RX32\n", RT_LE2H_U32(pInfo->idSecurity) ));
647 Log2(("NTFS: cbQuotaChared %#RX64\n", RT_LE2H_U64(pInfo->cbQuotaChared) ));
648 Log2(("NTFS: idxUpdateSequence %#RX64\n", RT_LE2H_U64(pInfo->idxUpdateSequence) ));
649 }
650 if (cbValue > sizeof(*pInfo))
651 Log2(("NTFS: Undefined data: %.*Rhxs\n", cbValue - sizeof(*pInfo), &pbValue[sizeof(*pInfo)]));
652 break;
653 }
654
655 case NTFS_AT_ATTRIBUTE_LIST:
656 {
657 uint32_t iEntry = 0;
658 uint32_t offEntry = 0;
659 while (offEntry + NTFSATLISTENTRY_SIZE_MINIMAL < cbValue)
660 {
661 PCNTFSATLISTENTRY pInfo = (PCNTFSATLISTENTRY)&pbValue[offEntry];
662 Log2(("NTFS: attr[%u]: %#x in %#RX64 (sqn %#x), instance %#x, VNC=%#RX64-, name %#x L %#x\n",
663 iEntry, RT_LE2H_U32(pInfo->uAttrType), NTFSMFTREF_GET_IDX(&pInfo->InMftRec),
664 NTFSMFTREF_GET_SEQ(&pInfo->InMftRec), RT_LE2H_U16(pInfo->idAttrib),
665 RT_LE2H_U64(pInfo->iVcnFirst), pInfo->offName, pInfo->cwcName));
666 if ( pInfo->cwcName > 0
667 && pInfo->offName < pInfo->cbEntry)
668 Log2(("NTFS: name '%.*ls'\n", pInfo->cwcName, (uint8_t *)pInfo + pInfo->offName));
669
670 /* next */
671 if (pInfo->cbEntry < NTFSATLISTENTRY_SIZE_MINIMAL)
672 {
673 Log2(("NTFS: cbEntry is too small! cbEntry=%#x, min %#x\n",
674 pInfo->cbEntry, NTFSATLISTENTRY_SIZE_MINIMAL));
675 break;
676 }
677 iEntry++;
678 offEntry += RT_ALIGN_32(pInfo->cbEntry, 8);
679 }
680 break;
681 }
682
683 case NTFS_AT_FILENAME:
684 {
685 PCNTFSATFILENAME pInfo = (PCNTFSATFILENAME)pbValue;
686 if (cbValue >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
687 {
688 Log2(("NTFS: ParentDirMftRec %#RX64, sqn %#x\n",
689 NTFSMFTREF_GET_IDX(&pInfo->ParentDirMftRec), NTFSMFTREF_GET_SEQ(&pInfo->ParentDirMftRec) ));
690 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
691 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
692 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
693 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
694 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
695 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
696 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
697 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
698 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
699 RT_LE2H_U64(pInfo->cbAllocated), RT_LE2H_U64(pInfo->cbAllocated)));
700 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
701 RT_LE2H_U64(pInfo->cbData), RT_LE2H_U64(pInfo->cbData)));
702 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
703 if (RT_LE2H_U32(pInfo->fFileAttribs) & NTFS_FA_REPARSE_POINT)
704 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pInfo->u.uReparseTag) ));
705 else
706 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pInfo->u.cbPackedEas) ));
707 Log2(("NTFS: cwcFilename %#x\n", pInfo->cwcFilename));
708 Log2(("NTFS: fFilenameType %#x\n", pInfo->fFilenameType));
709 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pInfo->cwcFilename]) <= cbValue)
710 Log2(("NTFS: wszFilename '%.*ls'\n", pInfo->cwcFilename, pInfo->wszFilename ));
711 else
712 Log2(("NTFS: Error! Truncated filename!!\n"));
713 }
714 else
715 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATFILENAME!\n",
716 cbValue, RT_OFFSETOF(NTFSATFILENAME, wszFilename) ));
717 break;
718 }
719
720 //case NTFS_AT_OBJECT_ID:
721 //case NTFS_AT_SECURITY_DESCRIPTOR:
722 //case NTFS_AT_VOLUME_NAME:
723 //case NTFS_AT_VOLUME_INFORMATION:
724 //case NTFS_AT_DATA:
725
726 case NTFS_AT_INDEX_ROOT:
727 rtFsNtfsVol_LogIndexRoot((PCNTFSATINDEXROOT)pbValue, cbValue);
728 break;
729
730 //case NTFS_AT_INDEX_ALLOCATION:
731 //case NTFS_AT_BITMAP:
732 //case NTFS_AT_REPARSE_POINT:
733 //case NTFS_AT_EA_INFORMATION:
734 //case NTFS_AT_EA:
735 //case NTFS_AT_PROPERTY_SET:
736 //case NTFS_AT_LOGGED_UTILITY_STREAM:
737
738 default:
739 if (cbValue <= 24)
740 Log2(("NTFS: %.*Rhxs\n", cbValue, pbValue));
741 else
742 Log2(("%.*Rhxd\n", cbValue, pbValue));
743 break;
744 }
745
746 }
747 else
748 Log2(("NTFS: !Value is out of bounds!\n"));
749 }
750 else if (RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED) <= cbMaxAttrib)
751 {
752 Log2(("NTFS: VNC range %#RX64 .. %#RX64 (%#RX64 clusters)\n",
753 RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst), RT_LE2H_U64(pHdr->u.NonRes.iVcnLast),
754 RT_LE2H_U64(pHdr->u.NonRes.iVcnLast) - RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst) + 1));
755 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
756 RT_LE2H_U64(pHdr->u.NonRes.cbAllocated), RT_LE2H_U64(pHdr->u.NonRes.cbAllocated)));
757 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
758 RT_LE2H_U64(pHdr->u.NonRes.cbData), RT_LE2H_U64(pHdr->u.NonRes.cbData)));
759 Log2(("NTFS: cbInitialized %#RX64 (%Rhcb)\n",
760 RT_LE2H_U64(pHdr->u.NonRes.cbInitialized), RT_LE2H_U64(pHdr->u.NonRes.cbInitialized)));
761 uint16_t const offMappingPairs = RT_LE2H_U16(pHdr->u.NonRes.offMappingPairs);
762 Log2(("NTFS: offMappingPairs %#RX16\n", offMappingPairs));
763 if ( pHdr->u.NonRes.abReserved[0] || pHdr->u.NonRes.abReserved[1]
764 || pHdr->u.NonRes.abReserved[2] || pHdr->u.NonRes.abReserved[3] || pHdr->u.NonRes.abReserved[4] )
765 Log2(("NTFS: abReserved %.7Rhxs\n", pHdr->u.NonRes.abReserved));
766 if (pHdr->u.NonRes.uCompressionUnit != 0)
767 Log2(("NTFS: Compression unit 2^%u clusters\n", pHdr->u.NonRes.uCompressionUnit));
768
769 if ( cbMaxAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
770 && cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
771 && ( offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
772 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED))
773 Log2(("NTFS: cbCompressed %#RX64 (%Rhcb)\n",
774 RT_LE2H_U64(pHdr->u.NonRes.cbCompressed), RT_LE2H_U64(pHdr->u.NonRes.cbCompressed)));
775 else if ( pHdr->u.NonRes.uCompressionUnit != 0
776 && pHdr->u.NonRes.uCompressionUnit != 64
777 && pHdr->u.NonRes.iVcnFirst == 0)
778 Log2(("NTFS: !Error! Compressed attrib fields are out of bound!\n"));
779
780 if ( offMappingPairs < cbAttrib
781 && offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
782 {
783 uint8_t const *pbPairs = &pbRec[offRec + offMappingPairs];
784 uint32_t const cbMaxPairs = cbAttrib - offMappingPairs;
785 int64_t iVnc = pHdr->u.NonRes.iVcnFirst;
786 if (cbMaxPairs < 48)
787 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x %.*Rhxs\n", cbMaxPairs, cbMaxPairs, pbPairs));
788 else
789 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x\n%.*Rhxd\n", cbMaxPairs, cbMaxPairs, pbPairs));
790 if (!iVnc && !*pbPairs)
791 Log2(("NTFS: [0]: Empty\n"));
792 else
793 {
794 if (iVnc != 0)
795 Log2(("NTFS: [00/0x000]: VCN=%#012RX64 L %#012RX64 - not mapped\n", 0, iVnc));
796 int64_t iLnc = 0;
797 uint32_t iPair = 0;
798 uint32_t offPairs = 0;
799 while (offPairs < cbMaxPairs)
800 {
801 /* First byte: 4-bit length of each of the pair values */
802 uint8_t const bLengths = pbPairs[offPairs];
803 if (!bLengths)
804 break;
805 uint8_t const cbRun = (bLengths & 0x0f) + (bLengths >> 4);
806 if (offPairs + cbRun > cbMaxPairs)
807 {
808 Log2(("NTFS: [%02d/%#05x]: run overrun! cbRun=%#x bLengths=%#x offPairs=%#x cbMaxPairs=%#x\n",
809 iPair, offPairs, cbRun, bLengths, offPairs, cbMaxPairs));
810 break;
811 }
812 //Log2(("NTFS: @%#05x: %.*Rhxs\n", offPairs, cbRun + 1, &pbPairs[offPairs]));
813
814 /* Value 1: Number of (virtual) clusters in this run. */
815 int64_t cClustersInRun;
816 uint8_t cbNum = (bLengths & 0xf);
817 if (cbNum)
818 {
819 uint8_t const *pbNum = &pbPairs[offPairs + cbNum]; /* last byte */
820 cClustersInRun = (int8_t)*pbNum--;
821 while (cbNum-- > 1)
822 cClustersInRun = (cClustersInRun << 8) + *pbNum--;
823 }
824 else
825 cClustersInRun = -1;
826
827 /* Value 2: The logical cluster delta to get to the first cluster in the run. */
828 cbNum = bLengths >> 4;
829 if (cbNum)
830 {
831 uint8_t const *pbNum = &pbPairs[offPairs + cbNum + (bLengths & 0xf)]; /* last byte */
832 int64_t cLcnDelta = (int8_t)*pbNum--;
833 while (cbNum-- > 1)
834 cLcnDelta = (cLcnDelta << 8) + *pbNum--;
835 iLnc += cLcnDelta;
836 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => LNC=%#012RX64\n",
837 iPair, offPairs, iVnc, cClustersInRun, iLnc));
838 }
839 else
840 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => HOLE\n",
841 iPair, offPairs, iVnc, cClustersInRun));
842
843 /* Advance. */
844 iVnc += cClustersInRun;
845 offPairs += 1 + cbRun;
846 iPair++;
847 }
848 }
849 }
850 else if ( cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
851 && cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
852 {
853 Log2(("NTFS: Warning! Odd non-resident attribute size: %#x!\n", cbAttrib));
854 if (cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
855 Log2(("NTFS: @%05x: %.*Rhxs!\n", offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
856 cbAttrib - NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
857 &pbRec[offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED]));
858 }
859 }
860 else
861 Log2(("NTFS: !Attrib header is out of bound!\n"));
862
863 /* Advance. */
864 offRec += RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_RESIDENT);
865 }
866
867 /* Anything left? */
868 if (offRec < cbRecUsed)
869 Log2(("NTFS: @%#05x: Tail: %.*Rhxs\n", offRec, cbRecUsed - offRec, &pbRec[offRec]));
870 }
871 else
872 Log2(("NTFS: Unknown record type: %.4Rhxs\n", pFileRec));
873 }
874}
875#endif /* LOG_ENABLED */
876
877
878static int rtFsNtfsAttr_ParseExtents(PRTFSNTFSATTR pAttrib, PRTFSNTFSEXTENTS pExtents, uint8_t cClusterShift, int64_t iVcnFirst,
879 uint64_t cbVolume, PRTERRINFO pErrInfo, uint64_t idxMft, uint32_t offAttrib)
880{
881 PCNTFSATTRIBHDR pAttrHdr = pAttrib->pAttrHdr;
882 Assert(pAttrHdr->fNonResident);
883 Assert(pExtents->cExtents == 0);
884 Assert(pExtents->paExtents == NULL);
885
886 /** @todo Not entirely sure how to best detect empty mapping pair program.
887 * Not sure if this is a real problem as zero length stuff can be
888 * resident. */
889 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
890 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
891 if ( offMappingPairs != cbAttrib
892 && offMappingPairs != 0)
893 {
894 if (pAttrHdr->u.NonRes.iVcnFirst < iVcnFirst)
895 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
896 "Bad MFT record %#RX64: Attribute (@%#x) has a lower starting VNC than expected: %#RX64, %#RX64",
897 idxMft, offAttrib, pAttrHdr->u.NonRes.iVcnFirst, iVcnFirst);
898
899 if ( offMappingPairs >= cbAttrib
900 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
901 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
902 "Bad MFT record %#RX64: Mapping pair program for attribute (@%#x) is out of bounds: %#x, cbAttrib=%#x",
903 idxMft, offAttrib, offMappingPairs, cbAttrib);
904
905 /*
906 * Count the pairs.
907 */
908 uint8_t const * const pbPairs = (const uint8_t *)pAttrHdr + pAttrHdr->u.NonRes.offMappingPairs;
909 uint32_t const cbPairs = cbAttrib - offMappingPairs;
910 uint32_t offPairs = 0;
911 uint32_t cPairs = 0;
912 while (offPairs < cbPairs)
913 {
914 uint8_t const bLengths = pbPairs[offPairs];
915 if (bLengths)
916 {
917 uint8_t const cbRunField = bLengths & 0x0f;
918 uint8_t const cbLcnField = bLengths >> 4;
919 if ( cbRunField > 0
920 && cbRunField <= 8)
921 {
922 if (cbLcnField <= 8)
923 {
924 cPairs++;
925
926 /* Advance and check for overflow/end. */
927 offPairs += 1 + cbRunField + cbLcnField;
928 if (offPairs <= cbAttrib)
929 continue;
930 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
931 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x) is out of bounds",
932 idxMft, cPairs - 1, offAttrib);
933 }
934 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
935 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbLcnField is out of bound: %u",
936 idxMft, cPairs - 1, offAttrib, cbLcnField);
937
938 }
939 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
940 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbRunField is out of bound: %u",
941 idxMft, cPairs - 1, offAttrib, cbRunField);
942 }
943 break;
944 }
945
946 /*
947 * Allocate an the extent table for them.
948 */
949 uint32_t const cExtents = cPairs + (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst);
950 if (cExtents)
951 {
952 PRTFSNTFSEXTENT paExtents = (PRTFSNTFSEXTENT)RTMemAllocZ(sizeof(paExtents[0]) * cExtents);
953 AssertReturn(paExtents, VERR_NO_MEMORY);
954
955 /*
956 * Fill the table.
957 */
958 uint32_t iExtent = 0;
959
960 /* A sparse hole between this and the previous extent table? */
961 if (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst)
962 {
963 paExtents[iExtent].off = UINT64_MAX;
964 paExtents[iExtent].cbExtent = (pAttrHdr->u.NonRes.iVcnFirst - iVcnFirst) << cClusterShift;
965 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
966 iExtent++;
967 }
968
969 /* Run the program again, now with values and without verbose error checking. */
970 uint64_t cMaxClustersInRun = (INT64_MAX >> cClusterShift) - pAttrHdr->u.NonRes.iVcnFirst;
971 uint64_t cbData = 0;
972 int64_t iLcn = 0;
973 int rc = VINF_SUCCESS;
974 offPairs = 0;
975 for (; iExtent < cExtents; iExtent++)
976 {
977 uint8_t const bLengths = pbPairs[offPairs++];
978 uint8_t const cbRunField = bLengths & 0x0f;
979 uint8_t const cbLcnField = bLengths >> 4;
980 AssertBreakStmt((unsigned)cbRunField - 1U <= 7U, rc = VERR_VFS_BOGUS_FORMAT);
981 AssertBreakStmt((unsigned)cbLcnField <= 8U, rc = VERR_VFS_BOGUS_FORMAT);
982
983 AssertBreakStmt(!(pbPairs[offPairs + cbRunField - 1] & 0x80),
984 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
985 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): Negative runlength value",
986 idxMft, iExtent, offAttrib));
987 uint64_t cClustersInRun = 0;
988 switch (cbRunField)
989 {
990 case 8: cClustersInRun |= (uint64_t)pbPairs[offPairs + 7] << 56; RT_FALL_THRU();
991 case 7: cClustersInRun |= (uint64_t)pbPairs[offPairs + 6] << 48; RT_FALL_THRU();
992 case 6: cClustersInRun |= (uint64_t)pbPairs[offPairs + 5] << 40; RT_FALL_THRU();
993 case 5: cClustersInRun |= (uint64_t)pbPairs[offPairs + 4] << 32; RT_FALL_THRU();
994 case 4: cClustersInRun |= (uint32_t)pbPairs[offPairs + 3] << 24; RT_FALL_THRU();
995 case 3: cClustersInRun |= (uint32_t)pbPairs[offPairs + 2] << 16; RT_FALL_THRU();
996 case 2: cClustersInRun |= (uint16_t)pbPairs[offPairs + 1] << 8; RT_FALL_THRU();
997 case 1: cClustersInRun |= (uint16_t)pbPairs[offPairs + 0] << 0; RT_FALL_THRU();
998 }
999 offPairs += cbRunField;
1000 AssertBreakStmt(cClustersInRun <= cMaxClustersInRun,
1001 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1002 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): too many clusters %#RX64, max %#RX64",
1003 idxMft, iExtent, offAttrib, cClustersInRun, cMaxClustersInRun));
1004 cMaxClustersInRun -= cClustersInRun;
1005 paExtents[iExtent].cbExtent = cClustersInRun << cClusterShift;
1006 cbData += cClustersInRun << cClusterShift;
1007
1008 if (cbLcnField)
1009 {
1010 unsigned offVncDelta = cbLcnField;
1011 int64_t cLncDelta = (int8_t)pbPairs[--offVncDelta + offPairs];
1012 while (offVncDelta-- > 0)
1013 cLncDelta = (cLncDelta << 8) | pbPairs[offVncDelta + offPairs];
1014 offPairs += cbLcnField;
1015
1016 iLcn += cLncDelta;
1017 if (iLcn >= 0)
1018 {
1019 paExtents[iExtent].off = (uint64_t)iLcn << cClusterShift;
1020 AssertBreakStmt((paExtents[iExtent].off >> cClusterShift) == (uint64_t)iLcn,
1021 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1022 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): iLcn %RX64 overflows when shifted by %u",
1023 idxMft, iExtent, offAttrib, iLcn, cClusterShift));
1024 AssertBreakStmt( paExtents[iExtent].off < cbVolume
1025 || paExtents[iExtent].cbExtent < cbVolume
1026 || paExtents[iExtent].off + paExtents[iExtent].cbExtent <= cbVolume,
1027 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1028 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x) outside volume: %#RX64 LB %#RX64, cbVolume=%#RX64",
1029 idxMft, iExtent, offAttrib, paExtents[iExtent].off,
1030 paExtents[iExtent].cbExtent, cbVolume));
1031 }
1032 else
1033 paExtents[iExtent].off = UINT64_MAX;
1034 }
1035 else
1036 paExtents[iExtent].off = UINT64_MAX;
1037 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
1038 }
1039
1040 /* Commit if everything went fine? */
1041 if (RT_SUCCESS(rc))
1042 {
1043 pExtents->cbData = cbData;
1044 pExtents->cExtents = cExtents;
1045 pExtents->paExtents = paExtents;
1046 }
1047 else
1048 {
1049 RTMemFree(paExtents);
1050 return rc;
1051 }
1052 }
1053 }
1054 return VINF_SUCCESS;
1055}
1056
1057
1058/**
1059 * Parses the given MTF record and all related records, putting the result in
1060 * pRec->pCore (with one reference for the caller).
1061 *
1062 * ASSUMES caller will release pRec->pCore on failure.
1063 *
1064 * @returns IPRT status code.
1065 * @param pThis The volume.
1066 * @param pRec The MFT record to parse.
1067 * @param pErrInfo Where to return additional error information. Optional.
1068 */
1069static int rtFsNtfsVol_ParseMft(PRTFSNTFSVOL pThis, PRTFSNTFSMFTREC pRec, PRTERRINFO pErrInfo)
1070{
1071 AssertReturn(!pRec->pCore, VERR_INTERNAL_ERROR_4);
1072
1073 /*
1074 * Check that it is a file record and that its base MFT record number is zero.
1075 * Caller should do the base record resolving.
1076 */
1077 PNTFSRECFILE pFileRec = pRec->pFileRec;
1078 if (pFileRec->Hdr.uMagic != NTFSREC_MAGIC_FILE)
1079 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1080 "Bad MFT record %#RX64: Not a FILE entry (%.4Rhxs)",
1081 pRec->TreeNode.Key, &pFileRec->Hdr);
1082 if (pFileRec->BaseMftRec.u64 != 0)
1083 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1084 "Bad MFT record %#RX64: Not a base record (%#RX64, sqn %#x)",
1085 pRec->TreeNode.Key, NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec),
1086 NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec) );
1087
1088 /*
1089 * Create a core node (1 reference, returned even on error).
1090 */
1091 PRTFSNTFSCORE pCore = (PRTFSNTFSCORE)RTMemAllocZ(sizeof(*pCore));
1092 AssertReturn(pCore, VERR_NO_MEMORY);
1093
1094 pCore->cRefs = 1;
1095 pCore->pVol = pThis;
1096 RTListInit(&pCore->AttribHead);
1097 pCore->pMftRec = pRec;
1098 rtFsNtfsMftRec_Retain(pRec);
1099 pRec->pCore = pCore;
1100
1101 /*
1102 * Parse attributes.
1103 * We process any attribute list afterwards, skipping attributes in this MFT record.
1104 */
1105 PRTFSNTFSATTR pAttrList = NULL;
1106 uint8_t * const pbRec = pRec->pbRec;
1107 uint32_t offRec = pFileRec->offFirstAttrib;
1108 uint32_t const cbRecUsed = RT_MIN(pThis->cbMftRecord, pFileRec->cbRecUsed);
1109 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
1110 {
1111 PNTFSATTRIBHDR pAttrHdr = (PNTFSATTRIBHDR)&pbRec[offRec];
1112
1113 /*
1114 * Validate the attribute data.
1115 */
1116 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
1117 uint32_t const cbMin = !pAttrHdr->fNonResident ? NTFSATTRIBHDR_SIZE_RESIDENT
1118 : pAttrHdr->u.NonRes.uCompressionUnit == 0 ? NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED
1119 : NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED;
1120 if (cbAttrib < cbMin)
1121 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1122 "Bad MFT record %#RX64: Attribute (@%#x) is too small (%#x, cbMin=%#x)",
1123 pRec->TreeNode.Key, offRec, cbAttrib, cbMin);
1124 if (offRec + cbAttrib > cbRecUsed)
1125 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1126 "Bad MFT record %#RX64: Attribute (@%#x) is too long (%#x, cbRecUsed=%#x)",
1127 pRec->TreeNode.Key, offRec, cbAttrib, cbRecUsed);
1128 if (cbAttrib & 0x7)
1129 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1130 "Bad MFT record %#RX64: Attribute (@%#x) size is misaligned: %#x",
1131 pRec->TreeNode.Key, offRec, cbAttrib);
1132 if (pAttrHdr->fNonResident)
1133 {
1134 int64_t const iVcnFirst = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnFirst);
1135 int64_t const iVcnLast = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnLast);
1136 if (iVcnFirst > iVcnLast)
1137 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1138 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is higher than iVcnLast (%#RX64)",
1139 pRec->TreeNode.Key, offRec, iVcnFirst, iVcnLast);
1140 if (iVcnFirst < 0)
1141 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1142 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is negative",
1143 pRec->TreeNode.Key, offRec, iVcnFirst);
1144 if ((uint64_t)iVcnLast > pThis->iMaxVirtualCluster)
1145 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1146 "Bad MFT record %#RX64: Attribute (@%#x): iVcnLast (%#RX64) is too high, max %RX64 (shift %#x)",
1147 pRec->TreeNode.Key, offRec, iVcnLast, pThis->cClusterShift, pThis->iMaxVirtualCluster);
1148 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
1149 if ( (offMappingPairs != 0 && offMappingPairs < cbMin)
1150 || offMappingPairs > cbAttrib)
1151 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1152 "Bad MFT record %#RX64: Attribute (@%#x): offMappingPairs (%#x) is out of bounds (cbAttrib=%#x, cbMin=%#x)",
1153 pRec->TreeNode.Key, offRec, offMappingPairs, cbAttrib, cbMin);
1154 if (pAttrHdr->u.NonRes.uCompressionUnit > 16)
1155 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1156 "Bad MFT record %#RX64: Attribute (@%#x): uCompressionUnit (%#x) is too high",
1157 pRec->TreeNode.Key, offRec, pAttrHdr->u.NonRes.uCompressionUnit);
1158
1159 int64_t const cbAllocated = RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated);
1160 if (cbAllocated < 0)
1161 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1162 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) is negative",
1163 pRec->TreeNode.Key, offRec, cbAllocated);
1164 if ((uint64_t)cbAllocated & (pThis->cbCluster - 1))
1165 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1166 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) isn't cluster aligned (cbCluster=%#x)",
1167 pRec->TreeNode.Key, offRec, cbAllocated, pThis->cbCluster);
1168
1169 int64_t const cbData = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1170 if (cbData < 0)
1171 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1172 "Bad MFT record %#RX64: Attribute (@%#x): cbData (%#RX64) is negative",
1173 pRec->TreeNode.Key, offRec, cbData);
1174
1175 int64_t const cbInitialized = RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized);
1176 if (cbInitialized < 0)
1177 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1178 "Bad MFT record %#RX64: Attribute (@%#x): cbInitialized (%#RX64) is negative",
1179 pRec->TreeNode.Key, offRec, cbInitialized);
1180 if (cbMin >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED)
1181 {
1182 int64_t const cbCompressed = RT_LE2H_U64(pAttrHdr->u.NonRes.cbCompressed);
1183 if (cbAllocated < 0)
1184 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1185 "Bad MFT record %#RX64: Attribute (@%#x): cbCompressed (%#RX64) is negative",
1186 pRec->TreeNode.Key, offRec, cbCompressed);
1187 }
1188 }
1189 else
1190 {
1191 uint16_t const offValue = RT_LE2H_U32(pAttrHdr->u.Res.offValue);
1192 if ( offValue > cbAttrib
1193 || offValue < NTFSATTRIBHDR_SIZE_RESIDENT)
1194 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1195 "Bad MFT record %#RX64: Attribute (@%#x): offValue (%#RX16) is out of bounds (cbAttrib=%#RX32, cbValue=%#RX32)",
1196 pRec->TreeNode.Key, offRec, offValue, cbAttrib, RT_LE2H_U32(pAttrHdr->u.Res.cbValue));
1197 if ((pAttrHdr->fFlags & NTFS_AF_COMPR_FMT_MASK) != NTFS_AF_COMPR_FMT_NONE)
1198 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1199 "Bad MFT record %#RX64: Attribute (@%#x): fFlags (%#RX16) indicate compression of a resident attribute",
1200 pRec->TreeNode.Key, offRec, RT_LE2H_U16(pAttrHdr->fFlags));
1201 }
1202
1203 if (pAttrHdr->cwcName != 0)
1204 {
1205 uint16_t offName = RT_LE2H_U16(pAttrHdr->offName);
1206 if ( offName < cbMin
1207 || offName >= cbAttrib)
1208 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1209 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) is out of bounds (cbAttrib=%#RX32, cbMin=%#RX32)",
1210 pRec->TreeNode.Key, offRec, offName, cbAttrib, cbMin);
1211 if (offName + pAttrHdr->cwcName * sizeof(RTUTF16) > cbAttrib)
1212 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1213 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) + cwcName (%#x) is out of bounds (cbAttrib=%#RX32)",
1214 pRec->TreeNode.Key, offRec, offName, pAttrHdr->cwcName, cbAttrib);
1215 }
1216
1217 /*
1218 * Allocate and initialize a new attribute.
1219 */
1220 PRTFSNTFSATTR pAttrib = (PRTFSNTFSATTR)RTMemAllocZ(sizeof(*pAttrib));
1221 AssertReturn(pAttrib, VERR_NO_MEMORY);
1222 pAttrib->pAttrHdr = pAttrHdr;
1223 pAttrib->offAttrHdrInMftRec = offRec;
1224 pAttrib->pCore = pCore;
1225 //pAttrib->cbResident = 0;
1226 //pAttrib->cbValue = 0;
1227 //pAttrib->Extents.cExtents = 0;
1228 //pAttrib->Extents.paExtents = NULL;
1229 //pAttrib->pSubRecHead = NULL;
1230 if (pAttrHdr->fNonResident)
1231 {
1232 pAttrib->cbValue = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1233 int rc = rtFsNtfsAttr_ParseExtents(pAttrib, &pAttrib->Extents, pThis->cClusterShift, 0 /*iVncFirst*/,
1234 pThis->cbVolume, pErrInfo, pRec->TreeNode.Key, offRec);
1235 if (RT_FAILURE(rc))
1236 {
1237 RTMemFree(pAttrib);
1238 return rc;
1239 }
1240 }
1241 else
1242 {
1243 pAttrib->cbValue = RT_LE2H_U32(pAttrHdr->u.Res.cbValue);
1244 if ( (uint32_t)pAttrib->cbValue > 0
1245 && RT_LE2H_U16(pAttrHdr->u.Res.offValue) < cbAttrib)
1246 {
1247 pAttrib->cbResident = cbAttrib - RT_LE2H_U16(pAttrHdr->u.Res.offValue);
1248 if (pAttrib->cbResident > (uint32_t)pAttrib->cbValue)
1249 pAttrib->cbResident = (uint32_t)pAttrib->cbValue;
1250 }
1251 }
1252
1253 RTListAppend(&pCore->AttribHead, &pAttrib->ListEntry);
1254
1255 if (pAttrHdr->uAttrType == NTFS_AT_ATTRIBUTE_LIST)
1256 pAttrList = pAttrib;
1257
1258 /* Advance. */
1259 offRec += cbAttrib;
1260 }
1261
1262 /*
1263 * Process any attribute list.
1264 */
1265 if (pAttrList)
1266 {
1267 /** @todo */
1268 }
1269
1270 return VINF_SUCCESS;
1271}
1272
1273
1274/**
1275 * Translates a attribute value offset to a disk offset.
1276 *
1277 * @returns Disk offset, UINT64_MAX if not translatable for some reason.
1278 * @param pAttr The
1279 * @param off The offset to translate.
1280 * @param pcbValid Where to return the run length at the return offset.
1281 * Optional.
1282 */
1283static uint64_t rtFsNtfsAttr_OffsetToDisk(PRTFSNTFSATTR pAttr, uint64_t off, uint64_t *pcbValid)
1284{
1285 /*
1286 * Searching the extend list is a tad complicated since it starts in one
1287 * structure and continues in a different one. But whatever.
1288 */
1289 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1290 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1291 for (;;)
1292 {
1293 if (off < pTable->cbData)
1294 {
1295 uint32_t iExtent = 0;
1296 while ( iExtent < pTable->cExtents
1297 && off >= pTable->paExtents[iExtent].cbExtent)
1298 {
1299 off -= pTable->paExtents[iExtent].cbExtent;
1300 iExtent++;
1301 }
1302 AssertReturn(iExtent < pTable->cExtents, UINT64_MAX);
1303 if (pcbValid)
1304 *pcbValid = pTable->paExtents[iExtent].cbExtent - off;
1305 return pTable->paExtents[iExtent].off != UINT64_MAX ? pTable->paExtents[iExtent].off + off : UINT64_MAX;
1306 }
1307
1308 /* Next table. */
1309 off -= pTable->cbData;
1310 if (!pCurSub)
1311 pCurSub = pAttr->pSubRecHead;
1312 else
1313 pCurSub = pCurSub->pNext;
1314 if (!pCurSub)
1315 {
1316 if (pcbValid)
1317 *pcbValid = 0;
1318 return UINT64_MAX;
1319 }
1320 pTable = &pCurSub->Extents;
1321 }
1322 /* not reached */
1323}
1324
1325
1326static int rtFsNtfsAttr_Read(PRTFSNTFSATTR pAttr, uint64_t off, void *pvBuf, size_t cbToRead)
1327{
1328 PRTFSNTFSVOL pVol = pAttr->pCore->pVol;
1329 int rc;
1330 if (!pAttr->pAttrHdr->fNonResident)
1331 {
1332 /*
1333 * The attribute is resident.
1334 */
1335 uint32_t cbAttrib = RT_LE2H_U32(pAttr->pAttrHdr->cbAttrib);
1336 uint32_t cbValue = RT_LE2H_U32(pAttr->pAttrHdr->u.Res.cbValue);
1337 uint16_t offValue = RT_LE2H_U16(pAttr->pAttrHdr->u.Res.offValue);
1338 if ( off < cbValue
1339 && cbToRead <= cbValue
1340 && off + cbToRead <= cbValue)
1341 {
1342 if (offValue <= cbAttrib)
1343 {
1344 cbAttrib -= offValue;
1345 if (off < cbAttrib)
1346 {
1347 /** @todo check if its possible to have cbValue larger than the attribute and
1348 * reading those extra bytes as zero. */
1349 if ( pAttr->offAttrHdrInMftRec + offValue + cbAttrib <= pVol->cbMftRecord
1350 && cbAttrib <= pVol->cbMftRecord)
1351 {
1352 size_t cbToCopy = cbAttrib - off;
1353 if (cbToCopy > cbToRead)
1354 cbToCopy = cbToRead;
1355 memcpy(pvBuf, (uint8_t *)pAttr->pAttrHdr + offValue, cbToCopy);
1356 pvBuf = (uint8_t *)pvBuf + cbToCopy;
1357 cbToRead -= cbToCopy;
1358 rc = VINF_SUCCESS;
1359 }
1360 else
1361 {
1362 rc = VERR_VFS_BOGUS_OFFSET;
1363 Log(("rtFsNtfsAttr_Read: bad resident attribute!\n"));
1364 }
1365 }
1366 else
1367 rc = VINF_SUCCESS;
1368 }
1369 else
1370 rc = VERR_VFS_BOGUS_FORMAT;
1371 }
1372 else
1373 rc = VERR_EOF;
1374 }
1375 else if (pAttr->pAttrHdr->u.NonRes.uCompressionUnit == 0)
1376 {
1377 /*
1378 * Uncompressed non-resident attribute.
1379 */
1380 uint64_t const cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
1381 if ( off >= cbAllocated
1382 || cbToRead > cbAllocated
1383 || off + cbToRead > cbAllocated)
1384 rc = VERR_EOF;
1385 else
1386 {
1387 rc = VINF_SUCCESS;
1388
1389 uint64_t const cbInitialized = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbInitialized);
1390 if ( off < cbInitialized
1391 && cbToRead > 0)
1392 {
1393 /*
1394 * Locate the first extent. This is a tad complicated.
1395 *
1396 * We move off along as we traverse the extent tables, so that it is relative
1397 * to the start of the current extent.
1398 */
1399 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1400 uint32_t iExtent = 0;
1401 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1402 for (;;)
1403 {
1404 if (off < pTable->cbData)
1405 {
1406 while ( iExtent < pTable->cExtents
1407 && off >= pTable->paExtents[iExtent].cbExtent)
1408 {
1409 off -= pTable->paExtents[iExtent].cbExtent;
1410 iExtent++;
1411 }
1412 AssertReturn(iExtent < pTable->cExtents, VERR_INTERNAL_ERROR_2);
1413 break;
1414 }
1415
1416 /* Next table. */
1417 off -= pTable->cbData;
1418 if (!pCurSub)
1419 pCurSub = pAttr->pSubRecHead;
1420 else
1421 pCurSub = pCurSub->pNext;
1422 if (!pCurSub)
1423 {
1424 iExtent = UINT32_MAX;
1425 break;
1426 }
1427 pTable = &pCurSub->Extents;
1428 iExtent = 0;
1429 }
1430
1431 /*
1432 * The read loop.
1433 */
1434 while (iExtent != UINT32_MAX)
1435 {
1436 uint64_t cbMaxRead = pTable->paExtents[iExtent].cbExtent;
1437 Assert(off < cbMaxRead);
1438 cbMaxRead -= off;
1439 size_t const cbThisRead = cbMaxRead >= cbToRead ? cbToRead : (size_t)cbMaxRead;
1440 if (pTable->paExtents[iExtent].off == UINT64_MAX)
1441 RT_BZERO(pvBuf, cbThisRead);
1442 else
1443 {
1444 rc = RTVfsFileReadAt(pVol->hVfsBacking, pTable->paExtents[iExtent].off + off, pvBuf, cbThisRead, NULL);
1445 Log4(("NTFS: Volume read: @%#RX64 LB %#zx -> %Rrc\n", pTable->paExtents[iExtent].off + off, cbThisRead, rc));
1446 if (RT_FAILURE(rc))
1447 break;
1448 }
1449 pvBuf = (uint8_t *)pvBuf + cbThisRead;
1450 cbToRead -= cbThisRead;
1451 if (!cbToRead)
1452 break;
1453
1454 /*
1455 * Advance to the next extent.
1456 */
1457 iExtent++;
1458 if (iExtent >= pTable->cExtents)
1459 {
1460 pCurSub = pCurSub ? pCurSub->pNext : pAttr->pSubRecHead;
1461 if (!pCurSub)
1462 break;
1463 pTable = &pCurSub->Extents;
1464 iExtent = 0;
1465 }
1466 }
1467 }
1468 }
1469 }
1470 else
1471 {
1472 LogRel(("rtFsNtfsAttr_Read: Compressed files are not supported\n"));
1473 rc = VERR_NOT_SUPPORTED;
1474 }
1475
1476 /*
1477 * Anything else beyond the end of what's stored/initialized?
1478 */
1479 if ( cbToRead > 0
1480 && RT_SUCCESS(rc))
1481 {
1482 RT_BZERO(pvBuf, cbToRead);
1483 }
1484
1485 return rc;
1486}
1487
1488
1489/**
1490 *
1491 * @returns
1492 * @param pRecHdr .
1493 * @param cbRec .
1494 * @param fRelaxedUsa .
1495 * @param pErrInfo .
1496 *
1497 * @see https://msdn.microsoft.com/en-us/library/bb470212%28v=vs.85%29.aspx
1498 */
1499static int rtFsNtfsRec_DoMultiSectorFixups(PNTFSRECHDR pRecHdr, uint32_t cbRec, bool fRelaxedUsa, PRTERRINFO pErrInfo)
1500{
1501 /*
1502 * Do sanity checking.
1503 */
1504 uint16_t offUpdateSeqArray = RT_LE2H_U16(pRecHdr->offUpdateSeqArray);
1505 uint16_t cUpdateSeqEntries = RT_LE2H_U16(pRecHdr->cUpdateSeqEntries);
1506 if ( !(cbRec & (NTFS_MULTI_SECTOR_STRIDE - 1))
1507 && !(offUpdateSeqArray & 1) /* two byte aligned */
1508 && cUpdateSeqEntries == 1 + cbRec / NTFS_MULTI_SECTOR_STRIDE
1509 && offUpdateSeqArray + (uint32_t)cUpdateSeqEntries * 2U < NTFS_MULTI_SECTOR_STRIDE - 2U)
1510 {
1511 uint16_t const *pauUsa = (uint16_t const *)((uint8_t *)pRecHdr + offUpdateSeqArray);
1512
1513 /*
1514 * The first update seqence array entry is the value stored at
1515 * the fixup locations at the end of the blocks. We read this
1516 * and check each of the blocks.
1517 */
1518 uint16_t const uCheck = *pauUsa++;
1519 cUpdateSeqEntries--;
1520 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1521 {
1522 uint16_t const *puBlockCheck = (uint16_t const *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1523 if (*puBlockCheck == uCheck)
1524 { /* likely */ }
1525 else if (!fRelaxedUsa)
1526 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1527 "Multisector transfer error: block #%u ends with %#x instead of %#x (fixup: %#x)",
1528 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) );
1529 else
1530 {
1531 Log(("NTFS: Multisector transfer warning: block #%u ends with %#x instead of %#x (fixup: %#x)\n",
1532 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) ));
1533 return VINF_SUCCESS;
1534 }
1535 }
1536
1537 /*
1538 * Apply the fixups.
1539 * Note! We advanced pauUsa above, so it's now at the fixup values.
1540 */
1541 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1542 {
1543 uint16_t *puFixup = (uint16_t *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1544 *puFixup = pauUsa[iBlock];
1545 }
1546 return VINF_SUCCESS;
1547 }
1548 if (fRelaxedUsa)
1549 {
1550 Log(("NTFS: Ignoring bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x\n",
1551 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries ));
1552 return VINF_SUCCESS;
1553 }
1554 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1555 "Bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x",
1556 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries);
1557}
1558
1559
1560/**
1561 * Allocate and parse an MFT record, returning a core object structure.
1562 *
1563 * @returns IPRT status code.
1564 * @param pThis The NTFS volume instance.
1565 * @param idxMft The index of the MTF record.
1566 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1567 * checks doesn't work or not present.
1568 * @param ppCore Where to return the core object structure.
1569 * @param pErrInfo Where to return error details. Optional.
1570 */
1571static int rtFsNtfsVol_NewCoreForMftIdx(PRTFSNTFSVOL pThis, uint64_t idxMft, bool fRelaxedUsa,
1572 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1573{
1574 *ppCore = NULL;
1575 Assert(pThis->pMftData);
1576 Assert(RTAvlU64Get(&pThis->MftRoot, idxMft) == NULL);
1577
1578 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, idxMft);
1579 AssertReturn(pRec, VERR_NO_MEMORY);
1580
1581 uint64_t offRec = idxMft * pThis->cbMftRecord;
1582 int rc = rtFsNtfsAttr_Read(pThis->pMftData, offRec, pRec->pbRec, pThis->cbMftRecord);
1583 if (RT_SUCCESS(rc))
1584 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, fRelaxedUsa, pErrInfo);
1585 if (RT_SUCCESS(rc))
1586 {
1587#ifdef LOG_ENABLED
1588 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
1589#endif
1590 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
1591 if (RT_SUCCESS(rc))
1592 {
1593 rtFsNtfsMftRec_Release(pRec, pThis);
1594 *ppCore = pRec->pCore;
1595 return VINF_SUCCESS;
1596 }
1597 rtFsNtfsCore_Release(pRec->pCore);
1598 rtFsNtfsMftRec_Release(pRec, pThis);
1599 }
1600 return rc;
1601}
1602
1603
1604/**
1605 * Queries the core object struct for the given MFT record reference.
1606 *
1607 * Does caching.
1608 *
1609 * @returns IPRT status code.
1610 * @param pThis The NTFS volume instance.
1611 * @param pMftRef The MFT reference to get the corresponding core
1612 * for.
1613 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1614 * checks doesn't work or not present.
1615 * @param ppCore Where to return the referenced core object
1616 * structure.
1617 * @param pErrInfo Where to return error details. Optional.
1618 */
1619static int rtFsNtfsVol_QueryCoreForMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pMftRef , bool fRelaxedUsa,
1620 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1621{
1622 *ppCore = NULL;
1623 Assert(pThis->pMftData);
1624
1625 int rc;
1626 PRTFSNTFSMFTREC pMftRec = (PRTFSNTFSMFTREC)RTAvlU64Get(&pThis->MftRoot, NTFSMFTREF_GET_IDX(pMftRef));
1627 if (pMftRec)
1628 {
1629 /*
1630 * Cache hit. Check that the resure sequence number matches.
1631 * To be slightly paranoid, also check that it's a base MFT record and that it has been parsed already.
1632 */
1633 if (RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1634 {
1635 if ( NTFSMFTREF_IS_ZERO(&pMftRec->pFileRec->BaseMftRec)
1636 && pMftRec->pCore)
1637 {
1638 rtFsNtfsCore_Retain(pMftRec->pCore);
1639 *ppCore = pMftRec->pCore;
1640 rc = VINF_SUCCESS;
1641 }
1642 else
1643 AssertLogRelMsgFailedStmt(("pCore=%p; BaseMftRec=%#RX64 sqn %#x\n", pMftRec->pCore,
1644 NTFSMFTREF_GET_IDX(&pMftRec->pFileRec->BaseMftRec),
1645 NTFSMFTREF_GET_SEQ(&pMftRec->pFileRec->BaseMftRec)),
1646 rc = VERR_INTERNAL_ERROR_3 );
1647 }
1648 else
1649 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1650 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1651 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1652 RT_LE2H_U16(pMftRec->pFileRec->uRecReuseSeqNo) );
1653 }
1654 else
1655 {
1656 /*
1657 * Load new and check that the reuse sequence number match.
1658 */
1659 rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFSMFTREF_GET_IDX(pMftRef), fRelaxedUsa, ppCore, pErrInfo);
1660 if (RT_SUCCESS(rc))
1661 {
1662 PRTFSNTFSCORE pCore = *ppCore;
1663 if (RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) == NTFSMFTREF_GET_SEQ(pMftRef))
1664 rc = VINF_SUCCESS;
1665 else
1666 {
1667 rtFsNtfsCore_Release(pCore);
1668 *ppCore = NULL;
1669 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1670 "Stale parent directory MFT reference: %#RX64 sqn %#x - current sqn %#x",
1671 NTFSMFTREF_GET_IDX(pMftRef), NTFSMFTREF_GET_SEQ(pMftRef),
1672 RT_LE2H_U16(pCore->pMftRec->pFileRec->uRecReuseSeqNo) );
1673 }
1674 }
1675 }
1676 return rc;
1677}
1678
1679
1680/**
1681 * Destroys a core structure.
1682 *
1683 * @returns 0
1684 * @param pThis The core structure.
1685 */
1686static uint32_t rtFsNtfsCore_Destroy(PRTFSNTFSCORE pThis)
1687{
1688 /*
1689 * Free attributes.
1690 */
1691 PRTFSNTFSATTR pCurAttr;
1692 PRTFSNTFSATTR pNextAttr;
1693 RTListForEachSafe(&pThis->AttribHead, pCurAttr, pNextAttr, RTFSNTFSATTR, ListEntry)
1694 {
1695 PRTFSNTFSATTRSUBREC pSub = pCurAttr->pSubRecHead;
1696 while (pSub)
1697 {
1698 pCurAttr->pSubRecHead = pSub->pNext;
1699 RTMemFree(pSub->Extents.paExtents);
1700 pSub->Extents.paExtents = NULL;
1701 pSub->pAttrHdr = NULL;
1702 pSub->pNext = NULL;
1703 RTMemFree(pSub);
1704
1705 pSub = pCurAttr->pSubRecHead;
1706 }
1707
1708 pCurAttr->pCore = NULL;
1709 pCurAttr->pAttrHdr = NULL;
1710 RTMemFree(pCurAttr->Extents.paExtents);
1711 pCurAttr->Extents.paExtents = NULL;
1712 }
1713
1714 /*
1715 * Release the MFT chain.
1716 */
1717 PRTFSNTFSMFTREC pMftRec = pThis->pMftRec;
1718 while (pMftRec)
1719 {
1720 pThis->pMftRec = pMftRec->pNext;
1721 Assert(pMftRec->pCore == pThis);
1722 pMftRec->pNext = NULL;
1723 pMftRec->pCore = NULL;
1724 rtFsNtfsMftRec_Release(pMftRec, pThis->pVol);
1725
1726 pMftRec = pThis->pMftRec;
1727 }
1728
1729 RTMemFree(pThis);
1730
1731 return 0;
1732}
1733
1734
1735/**
1736 * Releases a refernece to a core structure, maybe destroying it.
1737 *
1738 * @returns New reference count.
1739 * @param pThis The core structure.
1740 */
1741static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis)
1742{
1743 if (pThis)
1744 {
1745 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1746 Assert(cRefs < 128);
1747 if (cRefs != 0)
1748 return cRefs;
1749 return rtFsNtfsCore_Destroy(pThis);
1750 }
1751 return 0;
1752}
1753
1754
1755/**
1756 * Retains a refernece to a core structure.
1757 *
1758 * @returns New reference count.
1759 * @param pThis The core structure.
1760 */
1761static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis)
1762{
1763 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1764 Assert(cRefs < 128);
1765 return cRefs;
1766}
1767
1768
1769/**
1770 * Finds an unnamed attribute.
1771 *
1772 * @returns Pointer to the attribute structure if found, NULL if not.
1773 * @param pThis The core object structure to search.
1774 * @param uAttrType The attribute type to find.
1775 */
1776static PRTFSNTFSATTR rtFsNtfsCore_FindUnnamedAttribute(PRTFSNTFSCORE pThis, uint32_t uAttrType)
1777{
1778 PRTFSNTFSATTR pCurAttr;
1779 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1780 {
1781 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1782 if ( pAttrHdr->uAttrType == uAttrType
1783 && pAttrHdr->cwcName == 0)
1784 return pCurAttr;
1785 }
1786 return NULL;
1787}
1788
1789
1790/**
1791 * Finds a named attribute, case insensitive ASCII variant.
1792 *
1793 * @returns Pointer to the attribute structure if found, NULL if not.
1794 * @param pThis The core object structure to search.
1795 * @param uAttrType The attribute type to find.
1796 * @param pszAttrib The attribute name, predefined 7-bit ASCII name.
1797 * @param cchAttrib The length of the attribute.
1798 */
1799static PRTFSNTFSATTR rtFsNtfsCore_FindNamedAttributeAscii(PRTFSNTFSCORE pThis, uint32_t uAttrType,
1800 const char *pszAttrib, size_t cchAttrib)
1801{
1802 Assert(cchAttrib > 0);
1803 PRTFSNTFSATTR pCurAttr;
1804 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1805 {
1806 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1807 if ( pAttrHdr->uAttrType == uAttrType
1808 && pAttrHdr->cwcName == cchAttrib
1809 && RTUtf16NICmpAscii(NTFSATTRIBHDR_GET_NAME(pAttrHdr), pszAttrib, cchAttrib) == 0)
1810 return pCurAttr;
1811 }
1812 return NULL;
1813}
1814
1815
1816
1817/*
1818 *
1819 * NTFS directory code.
1820 * NTFS directory code.
1821 * NTFS directory code.
1822 *
1823 */
1824
1825#ifdef LOG_ENABLED
1826
1827/**
1828 * Logs an index header and all the entries.
1829 *
1830 * @param pIdxHdr The index header.
1831 * @param cbIndex The number of valid bytes starting with the header.
1832 * @param offIndex The offset of the index header into the parent
1833 * structure.
1834 * @param pszPrefix The log prefix.
1835 * @param uIdxType The index type.
1836 */
1837static void rtFsNtfsVol_LogIndexHdrAndEntries(PCNTFSINDEXHDR pIdxHdr, uint32_t cbIndex, uint32_t offIndex,
1838 const char *pszPrefix, uint32_t uIdxType)
1839{
1840 if (!LogIs2Enabled())
1841 return;
1842
1843 /*
1844 * Do the header.
1845 */
1846 if (cbIndex <= sizeof(*pIdxHdr))
1847 {
1848 Log2(("NTFS: %s: Error! Not enough space for the index header! cbIndex=%#x, index head needs %#x\n",
1849 pszPrefix, cbIndex, sizeof(*pIdxHdr)));
1850 return;
1851 }
1852
1853 Log2(("NTFS: %s: offFirstEntry %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->offFirstEntry),
1854 RT_LE2H_U32(pIdxHdr->offFirstEntry) >= cbIndex ? " !out-of-bounds!" : ""));
1855 Log2(("NTFS: %s: cbUsed %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbUsed),
1856 RT_LE2H_U32(pIdxHdr->cbUsed) > cbIndex ? " !out-of-bounds!" : ""));
1857 Log2(("NTFS: %s: cbAllocated %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbAllocated),
1858 RT_LE2H_U32(pIdxHdr->cbAllocated) > cbIndex ? " !out-of-bounds!" : ""));
1859 Log2(("NTFS: %s: fFlags %#x (%s%s)\n", pszPrefix, pIdxHdr->fFlags,
1860 pIdxHdr->fFlags & NTFSINDEXHDR_F_INTERNAL ? "internal" : "leaf",
1861 pIdxHdr->fFlags & ~NTFSINDEXHDR_F_INTERNAL ? " !!unknown-flags!!" : ""));
1862 if (pIdxHdr->abReserved[0]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[0]));
1863 if (pIdxHdr->abReserved[1]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[1]));
1864 if (pIdxHdr->abReserved[2]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[2]));
1865
1866 /*
1867 * The entries.
1868 */
1869 bool fSeenEnd = false;
1870 uint32_t iEntry = 0;
1871 uint32_t offCurEntry = RT_LE2H_U32(pIdxHdr->offFirstEntry);
1872 while (offCurEntry < cbIndex)
1873 {
1874 if (offCurEntry + sizeof(NTFSIDXENTRYHDR) > cbIndex)
1875 {
1876 Log2(("NTFS: Entry[%#04x]: Out of bounds: %#x LB %#x, max %#x\n",
1877 iEntry, offCurEntry, sizeof(NTFSIDXENTRYHDR), cbIndex));
1878 break;
1879 }
1880 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIdxHdr + offCurEntry);
1881 Log2(("NTFS: [%#04x]: @%#05x/@%#05x cbEntry=%#x cbKey=%#x fFlags=%#x (%s%s%s)\n",
1882 iEntry, offCurEntry, offCurEntry + offIndex, RT_LE2H_U16(pEntryHdr->cbEntry), RT_LE2H_U16(pEntryHdr->cbKey),
1883 RT_LE2H_U16(pEntryHdr->fFlags),
1884 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? "internal" : "leaf",
1885 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END ? " end" : "",
1886 pEntryHdr->fFlags & ~(NTFSIDXENTRYHDR_F_INTERNAL | NTFSIDXENTRYHDR_F_END) ? " !unknown!" : ""));
1887 if (uIdxType == NTFSATINDEXROOT_TYPE_DIR)
1888 Log2(("NTFS: FileMftRec %#RX64 sqn %#x\n",
1889 NTFSMFTREF_GET_IDX(&pEntryHdr->u.FileMftRec), NTFSMFTREF_GET_SEQ(&pEntryHdr->u.FileMftRec) ));
1890 else
1891 Log2(("NTFS: offData=%#x cbData=%#x uReserved=%#x\n",
1892 RT_LE2H_U16(pEntryHdr->u.View.offData), RT_LE2H_U16(pEntryHdr->u.View.cbData),
1893 RT_LE2H_U32(pEntryHdr->u.View.uReserved) ));
1894 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
1895 Log2(("NTFS: Subnode=%#RX64\n", RT_LE2H_U64(NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr)) ));
1896
1897 if ( RT_LE2H_U16(pEntryHdr->cbKey) >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename)
1898 && uIdxType == NTFSATINDEXROOT_TYPE_DIR)
1899 {
1900 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
1901 RTTIMESPEC Spec;
1902 char sz[80];
1903 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iCreationTime),
1904 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iCreationTime)), sz, sizeof(sz)) ));
1905 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastDataModTime),
1906 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastDataModTime)), sz, sizeof(sz)) ));
1907 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastMftModTime),
1908 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastMftModTime)), sz, sizeof(sz)) ));
1909 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastAccessTime),
1910 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastAccessTime)), sz, sizeof(sz)) ));
1911 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
1912 RT_LE2H_U64(pFilename->cbAllocated), RT_LE2H_U64(pFilename->cbAllocated)));
1913 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
1914 RT_LE2H_U64(pFilename->cbData), RT_LE2H_U64(pFilename->cbData)));
1915 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pFilename->fFileAttribs) ));
1916 if (RT_LE2H_U32(pFilename->fFileAttribs) & NTFS_FA_REPARSE_POINT)
1917 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pFilename->u.uReparseTag) ));
1918 else
1919 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pFilename->u.cbPackedEas) ));
1920 Log2(("NTFS: cwcFilename %#x\n", pFilename->cwcFilename));
1921 Log2(("NTFS: fFilenameType %#x\n", pFilename->fFilenameType));
1922 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= RT_LE2H_U16(pEntryHdr->cbKey))
1923 Log2(("NTFS: wszFilename '%.*ls'\n", pFilename->cwcFilename, pFilename->wszFilename ));
1924 else
1925 Log2(("NTFS: Error! Truncated filename!!\n"));
1926 }
1927
1928
1929 /* next */
1930 iEntry++;
1931 offCurEntry += RT_LE2H_U16(pEntryHdr->cbEntry);
1932 fSeenEnd = RT_BOOL(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END);
1933 if (fSeenEnd || RT_LE2H_U16(pEntryHdr->cbEntry) < sizeof(*pEntryHdr))
1934 break;
1935 }
1936 if (!fSeenEnd)
1937 Log2(("NTFS: %s: Warning! Missing NTFSIDXENTRYHDR_F_END node!\n", pszPrefix));
1938}
1939
1940# if 0 /* unused */
1941static void rtFsNtfsVol_LogIndexNode(PCNTFSATINDEXALLOC pIdxNode, uint32_t cbIdxNode, uint32_t uType)
1942{
1943 if (!LogIs2Enabled())
1944 return;
1945 if (cbIdxNode < sizeof(*pIdxNode))
1946 Log2(("NTFS: Index Node: Error! Too small! cbIdxNode=%#x, index node needs %#x\n", cbIdxNode, sizeof(*pIdxNode)));
1947 else
1948 {
1949 Log2(("NTFS: Index Node: uMagic %#x\n", RT_LE2H_U32(pIdxNode->RecHdr.uMagic)));
1950 Log2(("NTFS: Index Node: UpdateSeqArray %#x L %#x\n",
1951 RT_LE2H_U16(pIdxNode->RecHdr.offUpdateSeqArray), RT_LE2H_U16(pIdxNode->RecHdr.cUpdateSeqEntries) ));
1952 Log2(("NTFS: Index Node: uLsn %#RX64\n", RT_LE2H_U64(pIdxNode->uLsn) ));
1953 Log2(("NTFS: Index Node: iSelfAddress %#RX64\n", RT_LE2H_U64(pIdxNode->iSelfAddress) ));
1954 if (pIdxNode->RecHdr.uMagic == NTFSREC_MAGIC_INDEX_ALLOC)
1955 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxNode->Hdr, cbIdxNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
1956 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "Index Node Hdr", uType);
1957 else
1958 Log2(("NTFS: Index Node: !Error! Invalid magic!\n"));
1959 }
1960}
1961# endif
1962
1963/**
1964 * Logs a index root structure and what follows (index header + entries).
1965 *
1966 * @param pIdxRoot The index root.
1967 * @param cbIdxRoot Number of valid bytes starting with @a pIdxRoot.
1968 */
1969static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot)
1970{
1971 if (!LogIs2Enabled())
1972 return;
1973 if (cbIdxRoot < sizeof(*pIdxRoot))
1974 Log2(("NTFS: Index Root: Error! Too small! cbIndex=%#x, index head needs %#x\n", cbIdxRoot, sizeof(*pIdxRoot)));
1975 else
1976 {
1977 Log2(("NTFS: Index Root: cbIdxRoot %#x\n", cbIdxRoot));
1978 Log2(("NTFS: Index Root: uType %#x %s\n", RT_LE2H_U32(pIdxRoot->uType),
1979 pIdxRoot->uType == NTFSATINDEXROOT_TYPE_VIEW ? "view"
1980 : pIdxRoot->uType == NTFSATINDEXROOT_TYPE_DIR ? "directory" : "!unknown!"));
1981 Log2(("NTFS: Index Root: uCollationRules %#x %s\n", RT_LE2H_U32(pIdxRoot->uCollationRules),
1982 pIdxRoot->uCollationRules == NTFS_COLLATION_BINARY ? "binary"
1983 : pIdxRoot->uCollationRules == NTFS_COLLATION_FILENAME ? "filename"
1984 : pIdxRoot->uCollationRules == NTFS_COLLATION_UNICODE_STRING ? "unicode-string"
1985 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32 ? "uint32"
1986 : pIdxRoot->uCollationRules == NTFS_COLLATION_SID ? "sid"
1987 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_PAIR ? "uint32-pair"
1988 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_SEQ ? "uint32-sequence" : "!unknown!"));
1989 Log2(("NTFS: Index Root: cbIndexNode %#x\n", RT_LE2H_U32(pIdxRoot->cbIndexNode) ));
1990 Log2(("NTFS: Index Root: cAddressesPerIndexNode %#x => cbNodeAddressingUnit=%#x\n",
1991 pIdxRoot->cAddressesPerIndexNode, RT_LE2H_U32(pIdxRoot->cbIndexNode) / RT_MAX(1, pIdxRoot->cAddressesPerIndexNode) ));
1992 if (pIdxRoot->abReserved[0]) Log2(("NTFS: Index Root: abReserved[0] %#x\n", pIdxRoot->abReserved[0]));
1993 if (pIdxRoot->abReserved[1]) Log2(("NTFS: Index Root: abReserved[1] %#x\n", pIdxRoot->abReserved[1]));
1994 if (pIdxRoot->abReserved[2]) Log2(("NTFS: Index Root: abReserved[2] %#x\n", pIdxRoot->abReserved[2]));
1995
1996 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxRoot->Hdr, cbIdxRoot - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr),
1997 RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), "Index Root Hdr", pIdxRoot->uType);
1998 }
1999}
2000
2001#endif /* LOG_ENABLED */
2002
2003
2004/**
2005 * Validates an index header.
2006 *
2007 * @returns IPRT status code.
2008 * @param pRootInfo Pointer to the index root info.
2009 * @param pNodeInfo Pointer to the node info structure to load.
2010 * @param pIndexHdr Pointer to the index header.
2011 * @param cbIndex Size of the index.
2012 * @param pErrInfo Where to return extra error info.
2013 * @param pszWhat Error prefix.
2014 */
2015static int rtFsNtfsVol_LoadIndexNodeInfo(PCRTFSNTFSIDXROOTINFO pRootInfo, PRTFSNTFSIDXNODEINFO pNodeInfo, PCNTFSINDEXHDR pIndexHdr,
2016 uint32_t cbIndex, PRTERRINFO pErrInfo, const char *pszWhat)
2017{
2018 uint32_t const cbMinIndex = sizeof(*pIndexHdr) + sizeof(NTFSIDXENTRYHDR);
2019 if (cbIndex < cbMinIndex)
2020 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2021 "%s: Not enough room for the index header and one entry header! cbIndex=%#x (cbMinIndex=%#x)",
2022 pszWhat, cbIndex, cbMinIndex);
2023 uint32_t const cbAllocated = RT_LE2H_U32(pIndexHdr->cbAllocated);
2024 if ( cbAllocated > cbIndex
2025 || cbAllocated < cbMinIndex
2026 || (cbAllocated & 7) )
2027 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2028 "%s: Bogus index allocation size: %#x (min %#x, max %#x, 8 byte aligned)",
2029 pszWhat, cbAllocated, cbMinIndex, cbIndex);
2030 uint32_t const cbUsed = RT_LE2H_U32(pIndexHdr->cbUsed);
2031 if ( cbUsed > cbAllocated
2032 || cbUsed < cbMinIndex
2033 || (cbUsed & 7) )
2034 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2035 "%s: Bogus index used size: %#x (min %#x, max %#x, 8 byte aligned)",
2036 pszWhat, cbUsed, cbMinIndex, cbAllocated);
2037 uint32_t const offFirstEntry = RT_LE2H_U32(pIndexHdr->offFirstEntry);
2038 if ( offFirstEntry < sizeof(*pIndexHdr)
2039 || offFirstEntry >= cbUsed - sizeof(NTFSIDXENTRYHDR)
2040 || (offFirstEntry & 7) )
2041 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2042 "%s: Bogus first entry offset: %#x (min %#x, max %#x, 8 byte aligned)",
2043 pszWhat, offFirstEntry, sizeof(*pIndexHdr), cbUsed - sizeof(NTFSIDXENTRYHDR));
2044
2045 /*
2046 * The index entries.
2047 */
2048 uint32_t const uType = pRootInfo->pRoot->uType;
2049 uint32_t offEntry = offFirstEntry;
2050 uint32_t iEntry = 0;
2051 for (;;)
2052 {
2053 if (offEntry + sizeof(NTFSIDXENTRYHDR) > cbUsed)
2054 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2055 "%s: Entry #%u is out of bound: offset %#x (cbUsed=%#x)",
2056 pszWhat, iEntry, offEntry, cbUsed);
2057 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIndexHdr + offEntry);
2058 uint16_t const cbEntry = RT_LE2H_U16(pEntryHdr->cbEntry);
2059 uint32_t const cbSubnodeAddr = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? sizeof(int64_t) : 0);
2060 uint32_t const cbMinEntry = sizeof(*pEntryHdr) + cbSubnodeAddr;
2061 if ( cbEntry < cbMinEntry
2062 || offEntry + cbEntry > cbUsed
2063 || (cbEntry & 7) )
2064 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2065 "%s: Entry #%u has a bogus size: %#x (min %#x, max %#x, 8 byte aligned)",
2066 pszWhat, iEntry, cbEntry, cbMinEntry, cbUsed - offEntry);
2067
2068 uint32_t const cbMaxKey = cbEntry - sizeof(*pEntryHdr) - cbSubnodeAddr;
2069 uint32_t const cbMinKey = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END) ? 0
2070 : uType == NTFSATINDEXROOT_TYPE_DIR ? RT_UOFFSETOF(NTFSATFILENAME, wszFilename) : 0;
2071 uint16_t const cbKey = RT_LE2H_U16(pEntryHdr->cbKey);
2072 if ( cbKey < cbMinKey
2073 || cbKey > cbMaxKey)
2074 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2075 "%s: Entry #%u has a bogus key size: %#x (min %#x, max %#x)",
2076 pszWhat, iEntry, cbKey, cbMinKey, cbMaxKey);
2077 if ( !(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2078 && uType == NTFSATINDEXROOT_TYPE_DIR)
2079 {
2080 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2081 if (RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) > cbKey)
2082 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2083 "%s: Entry #%u filename is out of bounds: cwcFilename=%#x -> %#x key, max %#x",
2084 pszWhat, iEntry, pFilename->cwcFilename,
2085 RT_UOFFSETOF(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]), cbKey);
2086 }
2087
2088 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2089 {
2090 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr);
2091 if ( (uint64_t)iSubnode >= pRootInfo->uEndNodeAddresses
2092 || (iSubnode & pRootInfo->fNodeAddressMisalign) )
2093 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2094 "%s: Entry #%u has bogus subnode address: %#RX64 (max %#RX64, misalign %#x)",
2095 pszWhat, iEntry, iSubnode, pRootInfo->uEndNodeAddresses,
2096 pRootInfo->fNodeAddressMisalign);
2097 }
2098
2099 /* Advance. */
2100 offEntry += cbEntry;
2101 iEntry++;
2102 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2103 break;
2104 }
2105
2106 /*
2107 * Popuplate the node info structure.
2108 */
2109 pNodeInfo->pIndexHdr = pIndexHdr;
2110 pNodeInfo->fInternal = RT_BOOL(pIndexHdr->fFlags & NTFSINDEXHDR_F_INTERNAL);
2111 if (pNodeInfo != &pRootInfo->NodeInfo)
2112 pNodeInfo->pVol = pRootInfo->NodeInfo.pVol;
2113 pNodeInfo->cEntries = iEntry;
2114 pNodeInfo->papEntries = (PCNTFSIDXENTRYHDR *)RTMemAlloc(iEntry * sizeof(pNodeInfo->papEntries[0]));
2115 if (pNodeInfo->papEntries)
2116 {
2117 PCNTFSIDXENTRYHDR pEntryHdr = NTFSINDEXHDR_GET_FIRST_ENTRY(pIndexHdr);
2118 for (iEntry = 0; iEntry < pNodeInfo->cEntries; iEntry++)
2119 {
2120 pNodeInfo->papEntries[iEntry] = pEntryHdr;
2121 pEntryHdr = NTFSIDXENTRYHDR_GET_NEXT(pEntryHdr);
2122 }
2123 return VINF_SUCCESS;
2124 }
2125 return VERR_NO_MEMORY;
2126}
2127
2128
2129/**
2130 * Creates a shared directory structure given a MFT core.
2131 *
2132 * @returns IPRT status code.
2133 * @param pThis The NTFS volume instance.
2134 * @param pCore The MFT core structure that's allegedly a directory.
2135 * (No reference consumed of course.)
2136 * @param ppSharedDir Where to return the pointer to the new shared directory
2137 * structure on success. (Referenced.)
2138 * @param pErrInfo Where to return additions error info. Optional.
2139 * @param pszWhat Context prefix for error reporting and logging.
2140 */
2141static int rtFsNtfsVol_NewSharedDirFromCore(PRTFSNTFSVOL pThis, PRTFSNTFSCORE pCore, PRTFSNTFSDIRSHRD *ppSharedDir,
2142 PRTERRINFO pErrInfo, const char *pszWhat)
2143{
2144 *ppSharedDir = NULL;
2145
2146 /*
2147 * Look for the index root and validate it.
2148 */
2149 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
2150 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2151 if (!pRootAttr)
2152 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: Found no INDEX_ROOT attribute named $I30", pszWhat);
2153 if (pRootAttr->pAttrHdr->fNonResident)
2154 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is is not resident", pszWhat);
2155 if (pRootAttr->cbResident < sizeof(NTFSATINDEXROOT))
2156 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is too small: %#x, min %#x ",
2157 pszWhat, pRootAttr->cbResident, sizeof(pRootAttr->cbResident));
2158
2159 PCNTFSATINDEXROOT pIdxRoot = (PCNTFSATINDEXROOT)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pRootAttr->pAttrHdr);
2160#ifdef LOG_ENABLED
2161 rtFsNtfsVol_LogIndexRoot(pIdxRoot, pRootAttr->cbResident);
2162#endif
2163 if (pIdxRoot->uType != NTFSATINDEXROOT_TYPE_DIR)
2164 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2165 "%s: Wrong INDEX_ROOT type for a directory: %#x, expected %#x",
2166 pszWhat, RT_LE2H_U32(pIdxRoot->uType), RT_LE2H_U32_C(NTFSATINDEXROOT_TYPE_DIR));
2167 if (pIdxRoot->uCollationRules != NTFS_COLLATION_FILENAME)
2168 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2169 "%s: Wrong collation rules for a directory: %#x, expected %#x",
2170 pszWhat, RT_LE2H_U32(pIdxRoot->uCollationRules), RT_LE2H_U32_C(NTFS_COLLATION_FILENAME));
2171 uint32_t cbIndexNode = RT_LE2H_U32(pIdxRoot->cbIndexNode);
2172 if (cbIndexNode < 512 || cbIndexNode > _64K || !RT_IS_POWER_OF_TWO(cbIndexNode))
2173 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2174 "%s: Bogus index node size: %#x (expected power of two between 512 and 64KB)",
2175 pszWhat, cbIndexNode);
2176 unsigned const cNodeAddressShift = cbIndexNode >= pThis->cbCluster ? pThis->cClusterShift : 9;
2177 if (((uint32_t)pIdxRoot->cAddressesPerIndexNode << cNodeAddressShift) != cbIndexNode)
2178 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2179 "%s: Bogus addresses per index node value: %#x (cbIndexNode=%#x cNodeAddressShift=%#x)",
2180 pszWhat, pIdxRoot->cAddressesPerIndexNode, cbIndexNode, cNodeAddressShift);
2181 AssertReturn(pRootAttr->uObj.pSharedDir == NULL, VERR_INTERNAL_ERROR_3);
2182
2183 /*
2184 * Check for the node data stream and related allocation bitmap.
2185 */
2186 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
2187 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2188 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
2189 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2190 if (pIndexAlloc && !pIndexBitmap)
2191 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2192 "%s: INDEX_ALLOCATION attribute without BITMAP", pszWhat);
2193 if (!pIndexAlloc && pIndexBitmap)
2194 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2195 "%s: BITMAP attribute without INDEX_ALLOCATION", pszWhat);
2196 uint64_t uNodeAddressEnd = 0;
2197 if (pIndexAlloc)
2198 {
2199 if (!pIndexAlloc->pAttrHdr->fNonResident)
2200 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ALLOCATION is resident", pszWhat);
2201 if (pIndexAlloc->cbValue & (cbIndexNode - 1))
2202 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2203 "%s: INDEX_ALLOCATION size isn't aligned on node boundrary: %#RX64, cbIndexNode=%#x",
2204 pszWhat, pIndexAlloc->cbValue, cbIndexNode);
2205 uint64_t const cNodes = pIndexAlloc->cbValue / cbIndexNode;
2206 if (pIndexBitmap->cbValue != (RT_ALIGN_64(cNodes, 64) >> 3))
2207 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2208 "%s: BITMAP size does not match INDEX_ALLOCATION: %#RX64, expected %#RX64 (cbIndexNode=%#x, cNodes=%#RX64)",
2209 pszWhat, pIndexBitmap->cbValue, RT_ALIGN_64(cNodes, 64) >> 3, cbIndexNode, cNodes);
2210 uNodeAddressEnd = cNodes * pIdxRoot->cAddressesPerIndexNode;
2211 }
2212
2213 /*
2214 * Create a directory instance.
2215 */
2216 PRTFSNTFSDIRSHRD pNewDir = (PRTFSNTFSDIRSHRD)RTMemAllocZ(sizeof(*pNewDir));
2217 if (!pNewDir)
2218 return VERR_NO_MEMORY;
2219
2220 pNewDir->cRefs = 1;
2221 rtFsNtfsCore_Retain(pCore);
2222 pNewDir->RootInfo.pRootAttr = pRootAttr;
2223 pNewDir->RootInfo.pRoot = pIdxRoot;
2224 pNewDir->RootInfo.pAlloc = pIndexAlloc;
2225 pNewDir->RootInfo.uEndNodeAddresses = uNodeAddressEnd;
2226 pNewDir->RootInfo.cNodeAddressByteShift = cNodeAddressShift;
2227 pNewDir->RootInfo.fNodeAddressMisalign = pIdxRoot->cAddressesPerIndexNode - 1;
2228 pNewDir->RootInfo.NodeInfo.pVol = pThis;
2229
2230 /*
2231 * Finally validate the index header and entries.
2232 */
2233 int rc = rtFsNtfsVol_LoadIndexNodeInfo(&pNewDir->RootInfo, &pNewDir->RootInfo.NodeInfo, &pIdxRoot->Hdr,
2234 pRootAttr->cbResident - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), pErrInfo, pszWhat);
2235 if (RT_SUCCESS(rc))
2236 {
2237 *ppSharedDir = pNewDir;
2238 pRootAttr->uObj.pSharedDir = pNewDir;
2239 return VINF_SUCCESS;
2240 }
2241 RTMemFree(pNewDir);
2242 rtFsNtfsCore_Release(pCore);
2243 return rc;
2244}
2245
2246
2247/**
2248 * Gets a shared directory structure given an MFT record reference, creating a
2249 * new one if necessary.
2250 *
2251 * @returns IPRT status code.
2252 * @param pThis The NTFS volume instance.
2253 * @param pDirMftRef The MFT record reference to follow.
2254 * @param ppSharedDir Where to return the shared directory structure
2255 * (referenced).
2256 * @param pErrInfo Where to return error details. Optional.
2257 * @param pszWhat Error/log prefix.
2258 */
2259static int rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pDirMftRef,
2260 PRTFSNTFSDIRSHRD *ppSharedDir, PRTERRINFO pErrInfo, const char *pszWhat)
2261{
2262 /*
2263 * Get the core structure for the MFT record and check that it's a directory we've got.
2264 */
2265 PRTFSNTFSCORE pCore;
2266 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, pDirMftRef, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2267 if (RT_SUCCESS(rc))
2268 {
2269 if (pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY)
2270 {
2271 /*
2272 * Locate the $I30 root index attribute as we associate the
2273 * pointer to the shared directory pointer with it.
2274 */
2275 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
2276 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2277 if (pRootAttr)
2278 {
2279 if (!pRootAttr->uObj.pSharedDir)
2280 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, ppSharedDir, pErrInfo, pszWhat);
2281 else
2282 {
2283 Assert(pRootAttr->uObj.pSharedDir->RootInfo.pRootAttr->pCore == pCore);
2284 rtFsNtfsDirShrd_Retain(pRootAttr->uObj.pSharedDir);
2285 *ppSharedDir = pRootAttr->uObj.pSharedDir;
2286 }
2287 }
2288 else
2289 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY,
2290 "%s: Found INDEX_ROOT attribute named $I30, even though NTFSRECFILE_F_DIRECTORY is set",
2291 pszWhat);
2292 }
2293 else
2294 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
2295 rtFsNtfsCore_Release(pCore);
2296 }
2297 return rc;
2298}
2299
2300
2301/**
2302 * Frees resource kept by an index node info structure.
2303 *
2304 * @param pNodeInfo The index node info structure to delelte.
2305 */
2306static void rtFsNtfsIdxNodeInfo_Delete(PRTFSNTFSIDXNODEINFO pNodeInfo)
2307{
2308 RTMemFree(pNodeInfo->papEntries);
2309 pNodeInfo->papEntries = NULL;
2310 pNodeInfo->pNode = NULL;
2311 pNodeInfo->pVol = NULL;
2312}
2313
2314
2315/**
2316 * Gets or loads the specified subnode.
2317 *
2318 * @returns IPRT status code.
2319 * @param pRootInfo The index root info.
2320 * @param iNode The address of the node being queried.
2321 * @param ppNode Where to return the referenced pointer to the node.
2322 */
2323static int rtFsNtfsIdxRootInfo_QueryNode(PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iNode, PRTFSNTFSIDXNODE *ppNode)
2324{
2325 /*
2326 * A bit of paranoia. These has been checked already when loading, but it
2327 * usually doesn't hurt too much to be careful.
2328 */
2329 AssertReturn(!(iNode & pRootInfo->fNodeAddressMisalign), VERR_VFS_BOGUS_OFFSET);
2330 AssertReturn((uint64_t)iNode < pRootInfo->uEndNodeAddresses, VERR_VFS_BOGUS_OFFSET);
2331 AssertReturn(pRootInfo->pAlloc, VERR_VFS_BOGUS_OFFSET);
2332
2333 /*
2334 * First translate the node address to a disk byte offset and check the index node cache.
2335 */
2336 uint64_t offNode = iNode << pRootInfo->cNodeAddressByteShift;
2337 uint64_t offNodeOnDisk = rtFsNtfsAttr_OffsetToDisk(pRootInfo->pAlloc, offNode, NULL);
2338 PRTFSNTFSIDXNODE pNode = (PRTFSNTFSIDXNODE)RTAvlU64Get(&pRootInfo->NodeInfo.pVol->IdxNodeCacheRoot, offNodeOnDisk);
2339 if (pNode)
2340 {
2341 rtFsNtfsIdxNode_Retain(pNode);
2342 *ppNode = pNode;
2343 return VINF_SUCCESS;
2344 }
2345
2346 /*
2347 * Need to create a load a new node.
2348 */
2349 pNode = (PRTFSNTFSIDXNODE)RTMemAllocZ(sizeof(*pNode));
2350 AssertReturn(pNode, VERR_NO_MEMORY);
2351
2352 pNode->TreeNode.Key = offNodeOnDisk;
2353 uint32_t cbIndexNode = RT_LE2H_U32(pRootInfo->pRoot->cbIndexNode);
2354 pNode->cbCost = sizeof(*pNode) + cbIndexNode;
2355 pNode->cRefs = 1;
2356 pNode->pNode = (PNTFSATINDEXALLOC)RTMemAllocZ(cbIndexNode);
2357 int rc;
2358 if (pNode->pNode)
2359 {
2360 rc = rtFsNtfsAttr_Read(pRootInfo->pAlloc, offNode, pNode->pNode, cbIndexNode);
2361 if (RT_SUCCESS(rc))
2362 {
2363 rc = VERR_VFS_BOGUS_FORMAT;
2364 if (pNode->pNode->RecHdr.uMagic != NTFSREC_MAGIC_INDEX_ALLOC)
2365 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Invalid node magic %#x\n",
2366 iNode, RT_LE2H_U32(pNode->pNode->RecHdr.uMagic) ));
2367 else if ((int64_t)RT_LE2H_U64(pNode->pNode->iSelfAddress) != iNode)
2368 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Wrong iSelfAddress: %#x\n",
2369 iNode, RT_LE2H_U64(pNode->pNode->iSelfAddress) ));
2370 else
2371 {
2372 rc = rtFsNtfsRec_DoMultiSectorFixups(&pNode->pNode->RecHdr, cbIndexNode, false /*fRelaxedUsa*/, NULL /*pErrInfo*/);
2373 if (RT_SUCCESS(rc))
2374 {
2375 /*
2376 * Validate/parse it
2377 */
2378#ifdef LOG_ENABLED
2379 rtFsNtfsVol_LogIndexHdrAndEntries(&pNode->pNode->Hdr,
2380 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2381 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "index node",
2382 pRootInfo->pRoot->uType);
2383#endif
2384 rc = rtFsNtfsVol_LoadIndexNodeInfo(pRootInfo, &pNode->NodeInfo, &pNode->pNode->Hdr,
2385 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2386 NULL /*pErrInfo*/, "index node");
2387 if (RT_SUCCESS(rc))
2388 {
2389 pNode->cbCost += pNode->NodeInfo.cEntries * sizeof(pNode->NodeInfo.papEntries[0]);
2390
2391 /*
2392 * Insert it into the cache.
2393 */
2394 bool fInsertOkay = RTAvlU64Insert(&pRootInfo->NodeInfo.pVol->IdxNodeCacheRoot, &pNode->TreeNode);
2395 Assert(fInsertOkay);
2396 if (fInsertOkay)
2397 {
2398 *ppNode = pNode;
2399 return VINF_SUCCESS;
2400 }
2401 }
2402 }
2403 }
2404 }
2405
2406 RTMemFree(pNode->pNode);
2407 pNode->pNode = NULL;
2408 }
2409 else
2410 rc = VERR_NO_MEMORY;
2411 RTMemFree(pNode);
2412 return rc;
2413}
2414
2415
2416/**
2417 * Frees resource kept by an index root info structure.
2418 *
2419 * @param pRootInfo The index root info structure to delete.
2420 */
2421static void rtFsNtfsIdxRootInfo_Delete(PRTFSNTFSIDXROOTINFO pRootInfo)
2422{
2423 rtFsNtfsIdxNodeInfo_Delete(&pRootInfo->NodeInfo);
2424 pRootInfo->pRootAttr->uObj.pSharedDir = NULL;
2425 rtFsNtfsCore_Release(pRootInfo->pRootAttr->pCore);
2426 pRootInfo->pRootAttr = NULL;
2427 pRootInfo->pAlloc = NULL;
2428 pRootInfo->pRoot = NULL;
2429}
2430
2431
2432/**
2433 * Destroys a shared directory structure when the reference count reached zero.
2434 *
2435 * @returns zero
2436 * @param pThis The shared directory structure to destroy.
2437 */
2438static uint32_t rtFsNtfsDirShrd_Destroy(PRTFSNTFSDIRSHRD pThis)
2439{
2440 rtFsNtfsIdxRootInfo_Delete(&pThis->RootInfo);
2441 RTMemFree(pThis);
2442 return 0;
2443}
2444
2445
2446/**
2447 * Releases a references to a shared directory structure.
2448 *
2449 * @returns New reference count.
2450 * @param pThis The shared directory structure.
2451 */
2452static uint32_t rtFsNtfsDirShrd_Release(PRTFSNTFSDIRSHRD pThis)
2453{
2454 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
2455 Assert(cRefs < 128);
2456 if (cRefs > 0)
2457 return cRefs;
2458 return rtFsNtfsDirShrd_Destroy(pThis);
2459}
2460
2461
2462/**
2463 * Retains a references to a shared directory structure.
2464 *
2465 * @returns New reference count.
2466 * @param pThis The shared directory structure.
2467 */
2468static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis)
2469{
2470 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
2471 Assert(cRefs > 1);
2472 Assert(cRefs < 128);
2473 return cRefs;
2474}
2475
2476
2477/**
2478 * Compares the two filenames in an case insentivie manner.
2479 *
2480 * @retval -1 if the first filename comes first
2481 * @retval 0 if equal
2482 * @retval 1 if the second filename comes first.
2483 *
2484 * @param pwszUpper1 The first filename, this has been uppercase already.
2485 * @param cwcUpper1 The length of the first filename.
2486 * @param pawcFilename2 The second filename to compare it with. Not zero
2487 * terminated.
2488 * @param cwcFilename2 The length of the second filename.
2489 * @param pawcUpcase The uppercase table. 64K entries.
2490 */
2491static int rtFsNtfsIdxComp_Filename(PCRTUTF16 pwszUpper1, uint8_t cwcUpper1, PCRTUTF16 pawcFilename2, uint8_t cwcFilename2,
2492 PCRTUTF16 const pawcUpcase)
2493{
2494 while (cwcUpper1 > 0 && cwcFilename2 > 0)
2495 {
2496 RTUTF16 uc1 = *pwszUpper1++;
2497 RTUTF16 uc2 = *pawcFilename2++;
2498 if ( uc1 != uc2
2499 && uc1 != pawcUpcase[uc2])
2500 return uc1 < uc2 ? -1 : 1;
2501
2502 /* Decrement the lengths and loop. */
2503 cwcUpper1--;
2504 cwcFilename2--;
2505 }
2506
2507 if (!cwcUpper1)
2508 return cwcFilename2 ? -1 : 0;
2509 return cwcFilename2 ? 1 : 0;
2510}
2511
2512
2513/**
2514 * Look up a name in the directory.
2515 *
2516 * @returns IPRT status code.
2517 * @param pShared The shared directory structure.
2518 * @param pszEntry The name to lookup.
2519 * @param ppFilename Where to return the pointer to the filename structure.
2520 * @param ppEntryHdr Where to return the poitner to the entry header
2521 * structure.
2522 * @param ppNode Where to return the pointer to the node the filename
2523 * structure resides in. This must be released. It will
2524 * be set to NULL if the name was found in the root node.
2525 */
2526static int rtFsNtfsDirShrd_Lookup(PRTFSNTFSDIRSHRD pShared, const char *pszEntry,
2527 PCNTFSATFILENAME *ppFilename, PCNTFSIDXENTRYHDR *ppEntryHdr, PRTFSNTFSIDXNODE *ppNode)
2528{
2529 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
2530
2531 *ppFilename = NULL;
2532 *ppEntryHdr = NULL;
2533 *ppNode = NULL;
2534 /** @todo do streams (split on ':') */
2535
2536 /*
2537 * Convert the filename to UTF16 and uppercase.
2538 */
2539 PCRTUTF16 const pawcUpcase = pVol->pawcUpcase;
2540 RTUTF16 wszFilename[256+4];
2541 PRTUTF16 pwszDst = wszFilename;
2542 PRTUTF16 pwszEnd = &wszFilename[255];
2543 const char *pszSrc = pszEntry;
2544 for (;;)
2545 {
2546 RTUNICP uc;
2547 int rc = RTStrGetCpEx(&pszSrc, &uc);
2548 if (RT_SUCCESS(rc))
2549 {
2550 if (uc != 0)
2551 {
2552 if (uc < _64K)
2553 uc = pawcUpcase[uc];
2554 pwszDst = RTUtf16PutCp(pwszDst, uc);
2555 if ((uintptr_t)pwszDst <= (uintptr_t)pwszEnd)
2556 { /* likely */ }
2557 else
2558 {
2559 Log(("rtFsNtfsDirShrd_Lookup: Filename too long '%s'\n", pszEntry));
2560 return VERR_FILENAME_TOO_LONG;
2561 }
2562 }
2563 else
2564 {
2565 *pwszDst = '\0';
2566 break;
2567 }
2568 }
2569 else
2570 {
2571 Log(("rtFsNtfsDirShrd_Lookup: Invalid UTF-8 encoding (%Rrc): %.*Rhxs\n", rc, strlen(pszEntry), pszEntry));
2572 return rc;
2573 }
2574 }
2575 uint8_t const cwcFilename = (uint8_t)(pwszDst - wszFilename);
2576
2577 /*
2578 * Do the tree traversal.
2579 */
2580 PRTFSNTFSIDXROOTINFO pRootInfo = &pShared->RootInfo;
2581 PRTFSNTFSIDXNODEINFO pNodeInfo = &pRootInfo->NodeInfo;
2582 PRTFSNTFSIDXNODE pNode = NULL;
2583 for (;;)
2584 {
2585 /*
2586 * Search it.
2587 */
2588 PCNTFSIDXENTRYHDR *papEntries = pNodeInfo->papEntries;
2589 uint32_t iEnd = pNodeInfo->cEntries;
2590 AssertReturn(iEnd > 0, VERR_INTERNAL_ERROR_3);
2591
2592 /* Exclude the end node from the serach as it doesn't have any key. */
2593 if (papEntries[iEnd - 1]->fFlags & NTFSIDXENTRYHDR_F_END)
2594 iEnd--;
2595
2596 uint32_t iEntry;
2597 if (1 /*iEnd < 8*/ )
2598 {
2599 if (iEnd > 0)
2600 {
2601 for (iEntry = 0; iEntry < iEnd; iEntry++)
2602 {
2603 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(papEntries[iEntry] + 1);
2604 int iDiff = rtFsNtfsIdxComp_Filename(wszFilename, cwcFilename, pFilename->wszFilename,
2605 pFilename->cwcFilename, pawcUpcase);
2606 if (iDiff > 0)
2607 { /* likely */ }
2608 else if (iDiff == 0)
2609 {
2610 *ppNode = pNode;
2611 *ppEntryHdr = papEntries[iEntry];
2612 *ppFilename = pFilename;
2613 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Found it!\n", pszEntry));
2614 return VINF_SUCCESS;
2615 }
2616 else
2617 {
2618 rtFsNtfsIdxNode_Release(pNode);
2619 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Not found!\n", pszEntry));
2620 return VERR_FILE_NOT_FOUND;
2621 }
2622 }
2623 }
2624 else
2625 iEntry = iEnd;
2626 }
2627 /* else: implement binary search */
2628
2629 /*
2630 * Decend thru node iEntry.
2631 *
2632 * We could be bold and ASSUME that there is always an END node, but we're
2633 * playing safe for now.
2634 */
2635 if (iEnd < pNodeInfo->cEntries)
2636 {
2637 PCNTFSIDXENTRYHDR pEntry = papEntries[iEntry];
2638 if (pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2639 {
2640 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntry);
2641 rtFsNtfsIdxNode_Release(pNode);
2642 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
2643 if (RT_SUCCESS(rc))
2644 {
2645 pNodeInfo = &pNode->NodeInfo;
2646 continue;
2647 }
2648 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n",
2649 pszEntry, iSubnode, rc));
2650 return rc;
2651 }
2652 }
2653 rtFsNtfsIdxNode_Release(pNode);
2654 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Not found! (#2)\n", pszEntry));
2655 return VERR_FILE_NOT_FOUND;
2656 }
2657
2658 /* not reached */
2659}
2660
2661
2662/**
2663 * Destroys an index node.
2664 *
2665 * This will remove it from the cache tree, however the caller must make sure
2666 * its not in the reuse list any more.
2667 *
2668 * @param pNode The node to destroy.
2669 */
2670static void rtFsNtfsIdxNode_Destroy(PRTFSNTFSIDXNODE pNode)
2671{
2672 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
2673
2674 /* Remove it from the volume node cache. */
2675 PAVLU64NODECORE pAssertRemove = RTAvlU64Remove(&pVol->IdxNodeCacheRoot, pNode->TreeNode.Key);
2676 Assert(pAssertRemove == &pNode->TreeNode); NOREF(pAssertRemove);
2677 pVol->cIdxNodes--;
2678 pVol->cbIdxNodes -= pNode->cbCost;
2679
2680 /* Destroy it. */
2681 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
2682 RTMemFree(pNode->pNode);
2683 pNode->pNode = NULL;
2684 RTMemFree(pNode);
2685}
2686
2687
2688/**
2689 * Trims the index node cache.
2690 *
2691 * @param pThis The NTFS volume instance which index node cache
2692 * needs trimming.
2693 */
2694static void rtFsNtfsIdxVol_TrimIndexNodeCache(PRTFSNTFSVOL pThis)
2695{
2696 while ( pThis->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE
2697 && pThis->cUnusedIdxNodes)
2698 {
2699 PRTFSNTFSIDXNODE pNode = RTListRemoveFirst(&pThis->IdxNodeUnusedHead, RTFSNTFSIDXNODE, UnusedListEntry);
2700 pThis->cUnusedIdxNodes--;
2701 rtFsNtfsIdxNode_Destroy(pNode);
2702 }
2703}
2704
2705
2706/**
2707 * Index node reference reached zero, put it in the unused list and trim the
2708 * cache.
2709 *
2710 * @returns zero
2711 * @param pNode The index node.
2712 */
2713static uint32_t rtFsNtfsIdxNode_MaybeDestroy(PRTFSNTFSIDXNODE pNode)
2714{
2715 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
2716 if (pVol)
2717 {
2718 RTListAppend(&pVol->IdxNodeUnusedHead, &pNode->UnusedListEntry);
2719 pVol->cUnusedIdxNodes++;
2720 if (pVol->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE)
2721 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
2722 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
2723 return 0;
2724 }
2725 /* not sure if this is needed yet... */
2726 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
2727 RTMemFree(pNode);
2728 return 0;
2729}
2730
2731
2732/**
2733 * Releases a reference to an index node.
2734 *
2735 * @returns New reference count.
2736 * @param pNode The index node to release. NULL is ignored.
2737 */
2738static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode)
2739{
2740 if (pNode)
2741 {
2742 uint32_t cRefs = ASMAtomicDecU32(&pNode->cRefs);
2743 Assert(cRefs < 128);
2744 if (cRefs > 0)
2745 return cRefs;
2746 return rtFsNtfsIdxNode_MaybeDestroy(pNode);
2747 }
2748 return 0;
2749}
2750
2751
2752/**
2753 * Retains a reference to an index node.
2754 *
2755 * This will remove it from the unused list if necessary.
2756 *
2757 * @returns New reference count.
2758 * @param pNode The index to reference.
2759 */
2760static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode)
2761{
2762 uint32_t cRefs = ASMAtomicIncU32(&pNode->cRefs);
2763 if (cRefs == 1)
2764 {
2765 RTListNodeRemove(&pNode->UnusedListEntry);
2766 pNode->NodeInfo.pVol->cUnusedIdxNodes--;
2767 }
2768 return cRefs;
2769}
2770
2771
2772
2773
2774/*
2775 *
2776 * Directory instance methods
2777 * Directory instance methods
2778 * Directory instance methods
2779 *
2780 */
2781
2782/**
2783 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2784 */
2785static DECLCALLBACK(int) rtFsNtfsDir_Close(void *pvThis)
2786{
2787 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
2788 LogFlow(("rtFsNtfsDir_Close(%p/%p)\n", pThis, pThis->pShared));
2789
2790 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
2791 pThis->pShared = NULL;
2792 if (pShared)
2793 rtFsNtfsDirShrd_Release(pShared);
2794
2795 while (pThis->cEnumStackEntries > 0)
2796 {
2797 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
2798 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
2799 pEntry->pNodeInfo = NULL;
2800 }
2801 RTMemFree(pThis->paEnumStack);
2802 pThis->paEnumStack = NULL;
2803 pThis->cEnumStackMaxDepth = 0;
2804
2805 return VINF_SUCCESS;
2806}
2807
2808
2809/**
2810 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2811 */
2812static DECLCALLBACK(int) rtFsNtfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2813{
2814 Log(("rtFsNtfsDir_QueryInfo\n"));
2815 //PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
2816 //return rtFsNtfsCore_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
2817 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
2818 return VERR_NOT_IMPLEMENTED;
2819}
2820
2821
2822/**
2823 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2824 */
2825static DECLCALLBACK(int) rtFsNtfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2826{
2827 Log(("rtFsNtfsDir_SetMode\n"));
2828 RT_NOREF(pvThis, fMode, fMask);
2829 return VERR_WRITE_PROTECT;
2830}
2831
2832
2833/**
2834 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2835 */
2836static DECLCALLBACK(int) rtFsNtfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2837 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2838{
2839 Log(("rtFsNtfsDir_SetTimes\n"));
2840 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2841 return VERR_WRITE_PROTECT;
2842}
2843
2844
2845/**
2846 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2847 */
2848static DECLCALLBACK(int) rtFsNtfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2849{
2850 Log(("rtFsNtfsDir_SetOwner\n"));
2851 RT_NOREF(pvThis, uid, gid);
2852 return VERR_WRITE_PROTECT;
2853}
2854
2855
2856/**
2857 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
2858 */
2859static DECLCALLBACK(int) rtFsNtfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
2860 uint32_t fFlags, PRTVFSOBJ phVfsObj)
2861{
2862 LogFlow(("rtFsNtfsDir_Open: pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
2863 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
2864 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
2865 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
2866 int rc;
2867
2868 /*
2869 * We cannot create or replace anything, just open stuff.
2870 */
2871 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
2872 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
2873 { /* likely */ }
2874 else
2875 return VERR_WRITE_PROTECT;
2876
2877 /*
2878 * Special cases '.' and '..'
2879 */
2880 if ( pszEntry[0] == '.'
2881 && ( pszEntry[1] == '\0'
2882 || ( pszEntry[1] == '.'
2883 && pszEntry[2] == '\0')))
2884 {
2885 if (!(fFlags & RTVFSOBJ_F_OPEN_DIRECTORY))
2886 return VERR_IS_A_DIRECTORY;
2887
2888 PRTFSNTFSDIRSHRD pSharedToOpen;
2889 if ( pszEntry[1] == '\0'
2890 || pVol->pRootDir == pShared)
2891 {
2892 pSharedToOpen = pShared;
2893 rtFsNtfsDirShrd_Retain(pSharedToOpen);
2894 rc = VINF_SUCCESS;
2895 }
2896 else
2897 {
2898 /* Find a parent mft reference */
2899 pSharedToOpen = NULL;
2900 rc = VERR_VFS_BOGUS_FORMAT;
2901 PRTFSNTFSCORE pCore = pShared->RootInfo.pRootAttr->pCore;
2902 PRTFSNTFSATTR pCurAttr;
2903 RTListForEach(&pCore->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
2904 {
2905 if ( pCurAttr->pAttrHdr->uAttrType == NTFS_AT_FILENAME
2906 && pCurAttr->cbResident >= sizeof(NTFSATFILENAME))
2907 {
2908 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pCurAttr->pAttrHdr);
2909 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pFilename->ParentDirMftRec,
2910 &pSharedToOpen, NULL /*pErrInfo*/, "..");
2911 break;
2912 }
2913 }
2914 }
2915 if (RT_SUCCESS(rc))
2916 {
2917 RTVFSDIR hVfsDir;
2918 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
2919 rtFsNtfsDirShrd_Release(pSharedToOpen);
2920 if (RT_SUCCESS(rc))
2921 {
2922 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2923 RTVfsDirRelease(hVfsDir);
2924 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2925 }
2926 }
2927 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
2928 return rc;
2929 }
2930
2931 /*
2932 * Lookup the index entry.
2933 */
2934 PRTFSNTFSIDXNODE pNode;
2935 PCNTFSIDXENTRYHDR pEntryHdr;
2936 PCNTFSATFILENAME pFilename;
2937 rc = rtFsNtfsDirShrd_Lookup(pShared, pszEntry, &pFilename, &pEntryHdr, &pNode);
2938 if (RT_SUCCESS(rc))
2939 {
2940 uint32_t fFileAttribs = RT_LE2H_U32(pFilename->fFileAttribs);
2941 switch (fFileAttribs & (NTFS_FA_DIRECTORY | NTFS_FA_REPARSE_POINT))
2942 {
2943 /*
2944 * File.
2945 */
2946 case 0:
2947 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
2948 {
2949 //RTVFSFILE hVfsFile;
2950 //rc = rtFsIsoFile_New9660(pVol, pShared, pDirRec, cDirRecs,
2951 // offDirRec, fOpen, uVersion, &hVfsFile);
2952 //if (RT_SUCCESS(rc))
2953 //{
2954 // *phVfsObj = RTVfsObjFromFile(hVfsFile);
2955 // RTVfsFileRelease(hVfsFile);
2956 // AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2957 //}
2958 rc = VERR_NOT_IMPLEMENTED;
2959 }
2960 else
2961 rc = VERR_IS_A_FILE;
2962 break;
2963
2964 /*
2965 * Directory
2966 */
2967 case NTFS_FA_DIRECTORY:
2968 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
2969 {
2970 PRTFSNTFSDIRSHRD pSharedToOpen;
2971 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pEntryHdr->u.FileMftRec,
2972 &pSharedToOpen, NULL, pszEntry);
2973 if (RT_SUCCESS(rc))
2974 {
2975 RTVFSDIR hVfsDir;
2976 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
2977 rtFsNtfsDirShrd_Release(pSharedToOpen);
2978 if (RT_SUCCESS(rc))
2979 {
2980 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2981 RTVfsDirRelease(hVfsDir);
2982 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2983 }
2984 }
2985 }
2986 else
2987 rc = VERR_IS_A_DIRECTORY;
2988 break;
2989
2990 /*
2991 * Possible symbolic links.
2992 */
2993 case NTFS_FA_REPARSE_POINT:
2994 case NTFS_FA_DIRECTORY | NTFS_FA_REPARSE_POINT:
2995 rc = VERR_NOT_IMPLEMENTED;
2996 break;
2997
2998 default:
2999 rc = VERR_IPE_NOT_REACHED_DEFAULT_CASE;
3000 break;
3001 }
3002 rtFsNtfsIdxNode_Release(pNode);
3003 }
3004
3005 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3006 return rc;
3007}
3008
3009
3010/**
3011 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3012 */
3013static DECLCALLBACK(int) rtFsNtfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3014{
3015 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3016 Log(("rtFsNtfsDir_CreateDir\n"));
3017 return VERR_WRITE_PROTECT;
3018}
3019
3020
3021/**
3022 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3023 */
3024static DECLCALLBACK(int) rtFsNtfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3025{
3026 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3027 Log(("rtFsNtfsDir_OpenSymlink\n"));
3028 return VERR_NOT_SUPPORTED;
3029}
3030
3031
3032/**
3033 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3034 */
3035static DECLCALLBACK(int) rtFsNtfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3036 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
3037{
3038 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
3039 Log(("rtFsNtfsDir_CreateSymlink\n"));
3040 return VERR_WRITE_PROTECT;
3041}
3042
3043
3044/**
3045 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
3046 */
3047static DECLCALLBACK(int) rtFsNtfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
3048{
3049 RT_NOREF(pvThis, pszEntry, fType);
3050 Log(("rtFsNtfsDir_UnlinkEntry\n"));
3051 return VERR_WRITE_PROTECT;
3052}
3053
3054
3055/**
3056 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
3057 */
3058static DECLCALLBACK(int) rtFsNtfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
3059{
3060 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
3061 Log(("rtFsNtfsDir_RenameEntry\n"));
3062 return VERR_WRITE_PROTECT;
3063}
3064
3065
3066/**
3067 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
3068 */
3069static DECLCALLBACK(int) rtFsNtfsDir_RewindDir(void *pvThis)
3070{
3071 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3072 LogFlow(("rtFsNtfsDir_RewindDir\n"));
3073
3074 while (pThis->cEnumStackEntries > 0)
3075 {
3076 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
3077 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
3078 pEntry->pNodeInfo = NULL;
3079 }
3080 pThis->fNoMoreFiles = false;
3081
3082 return VINF_SUCCESS;
3083}
3084
3085
3086/**
3087 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
3088 */
3089static DECLCALLBACK(int) rtFsNtfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
3090 RTFSOBJATTRADD enmAddAttr)
3091{
3092 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3093 if (pThis->fNoMoreFiles)
3094 return VERR_NO_MORE_FILES;
3095 Log(("rtFsNtfsDir_ReadDir\n"));
3096 //PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3097 NOREF(pvThis); NOREF(pDirEntry); NOREF(pcbDirEntry); NOREF(enmAddAttr);
3098 return VERR_NOT_IMPLEMENTED;
3099}
3100
3101
3102/**
3103 * NTFS file operations.
3104 */
3105static const RTVFSDIROPS g_rtFsNtfsDirOps =
3106{
3107 { /* Obj */
3108 RTVFSOBJOPS_VERSION,
3109 RTVFSOBJTYPE_DIR,
3110 "NTFS Dir",
3111 rtFsNtfsDir_Close,
3112 rtFsNtfsDir_QueryInfo,
3113 RTVFSOBJOPS_VERSION
3114 },
3115 RTVFSDIROPS_VERSION,
3116 0,
3117 { /* ObjSet */
3118 RTVFSOBJSETOPS_VERSION,
3119 RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
3120 rtFsNtfsDir_SetMode,
3121 rtFsNtfsDir_SetTimes,
3122 rtFsNtfsDir_SetOwner,
3123 RTVFSOBJSETOPS_VERSION
3124 },
3125 rtFsNtfsDir_Open,
3126 NULL /* pfnFollowAbsoluteSymlink */,
3127 NULL /* pfnOpenFile */,
3128 NULL /* pfnOpenDir */,
3129 rtFsNtfsDir_CreateDir,
3130 rtFsNtfsDir_OpenSymlink,
3131 rtFsNtfsDir_CreateSymlink,
3132 NULL /* pfnQueryEntryInfo */,
3133 rtFsNtfsDir_UnlinkEntry,
3134 rtFsNtfsDir_RenameEntry,
3135 rtFsNtfsDir_RewindDir,
3136 rtFsNtfsDir_ReadDir,
3137 RTVFSDIROPS_VERSION,
3138};
3139
3140
3141/**
3142 * Creates a new directory instance given a shared directory structure.
3143 *
3144 * @returns IPRT status code.
3145 * @param pThis The NTFS volume instance.
3146 * @param pSharedDir The shared directory structure to create a new
3147 * handle to.
3148 * @param phVfsDir Where to return the directory handle.
3149 */
3150static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir)
3151{
3152 PRTFSNTFSDIR pNewDir;
3153 int rc = RTVfsNewDir(&g_rtFsNtfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
3154 phVfsDir, (void **)&pNewDir);
3155 if (RT_SUCCESS(rc))
3156 {
3157 rtFsNtfsDirShrd_Retain(pSharedDir);
3158 pNewDir->pShared = pSharedDir;
3159 pNewDir->cEnumStackEntries = 0;
3160 pNewDir->cEnumStackMaxDepth = 0;
3161 pNewDir->paEnumStack = NULL;
3162 return VINF_SUCCESS;
3163 }
3164 return rc;
3165}
3166
3167
3168
3169/*
3170 *
3171 * Volume level code.
3172 * Volume level code.
3173 * Volume level code.
3174 *
3175 */
3176
3177
3178/**
3179 * Slow path for querying the allocation state of a cluster.
3180 *
3181 * @returns IPRT status code.
3182 * @param pThis The NTFS volume instance.
3183 * @param iCluster The cluster to query.
3184 * @param pfState Where to return the state.
3185 */
3186static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
3187{
3188 int rc;
3189 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData);
3190 uint64_t const offInBitmap = iCluster >> 3;
3191 if (offInBitmap < cbWholeBitmap)
3192 {
3193 if (!pThis->pvBitmap)
3194 {
3195 /*
3196 * Try cache the whole bitmap if it's not too large.
3197 */
3198 if ( cbWholeBitmap <= RTFSNTFS_MAX_WHOLE_BITMAP_CACHE
3199 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8))
3200 {
3201 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8);
3202 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
3203 if (pThis->pvBitmap)
3204 {
3205 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
3206 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap);
3207 if (RT_SUCCESS(rc))
3208 {
3209 pThis->iFirstBitmapCluster = 0;
3210 pThis->cBitmapClusters = pThis->cClusters;
3211 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster);
3212 return VINF_SUCCESS;
3213 }
3214 RTMemFree(pThis->pvBitmap);
3215 pThis->pvBitmap = NULL;
3216 pThis->cbBitmapAlloc = 0;
3217 return rc;
3218 }
3219 }
3220
3221 /*
3222 * Do a cluster/4K cache.
3223 */
3224 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K);
3225 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
3226 if (!pThis->pvBitmap)
3227 {
3228 pThis->cbBitmapAlloc = 0;
3229 return VERR_NO_MEMORY;
3230 }
3231 }
3232
3233 /*
3234 * Load a cache line.
3235 */
3236 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc));
3237 uint64_t offLoad = offInBitmap & ~(pThis->cbBitmapAlloc - 1);
3238 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc);
3239
3240 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
3241 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad);
3242 if (RT_SUCCESS(rc))
3243 {
3244 pThis->iFirstBitmapCluster = offLoad << 3;
3245 pThis->cBitmapClusters = cbLoad << 3;
3246 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster));
3247 return VINF_SUCCESS;
3248 }
3249 pThis->cBitmapClusters = 0;
3250 }
3251 else
3252 {
3253 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap));
3254 rc = VERR_OUT_OF_RANGE;
3255 }
3256 return rc;
3257}
3258
3259
3260/**
3261 * Query the allocation state of the given cluster.
3262 *
3263 * @returns IPRT status code.
3264 * @param pThis The NTFS volume instance.
3265 * @param iCluster The cluster to query.
3266 * @param pfState Where to return the state.
3267 */
3268static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
3269{
3270 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster;
3271 if (iClusterInCache < pThis->cBitmapClusters)
3272 {
3273 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache);
3274 return VINF_SUCCESS;
3275 }
3276 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState);
3277}
3278
3279
3280
3281/**
3282 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
3283 */
3284static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
3285{
3286 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3287
3288 RTVfsFileRelease(pThis->hVfsBacking);
3289 pThis->hVfsBacking = NIL_RTVFSFILE;
3290 pThis->hVfsSelf = NIL_RTVFS;
3291
3292 return VINF_SUCCESS;
3293}
3294
3295
3296/**
3297 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
3298 */
3299static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3300{
3301 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
3302 return VERR_WRONG_TYPE;
3303}
3304
3305
3306/**
3307 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
3308 */
3309static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
3310{
3311 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3312 AssertReturn(pThis->pRootDir, VERR_INTERNAL_ERROR_4);
3313 int rc = rtFsNtfsVol_NewDirFromShared(pThis, pThis->pRootDir, phVfsDir);
3314 LogFlow(("rtFsNtfsVol_OpenRoot: returns %Rrc\n", rc));
3315 return rc;
3316}
3317
3318
3319/**
3320 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
3321 */
3322static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
3323{
3324 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
3325 *pfUsed = true;
3326
3327 /*
3328 * Round to a cluster range.
3329 */
3330 uint64_t iCluster = off >> pThis->cClusterShift;
3331
3332 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster));
3333 cb += off & (pThis->cbCluster - 1);
3334 cb = RT_ALIGN_Z(cb, pThis->cbCluster);
3335 size_t cClusters = cb >> pThis->cClusterShift;
3336
3337 /*
3338 * Check the clusters one-by-one.
3339 * Just to be cautious, we will always check the cluster at off, even when cb is zero.
3340 */
3341 do
3342 {
3343 bool fState = true;
3344 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
3345 if (RT_FAILURE(rc))
3346 return rc;
3347 if (fState)
3348 {
3349 *pfUsed = true;
3350 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
3351 return VINF_SUCCESS;
3352 }
3353
3354 iCluster++;
3355 } while (cClusters-- > 0);
3356
3357 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
3358 *pfUsed = false;
3359 return VINF_SUCCESS;
3360}
3361
3362
3363DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsNtfsVolOps =
3364{
3365 /* .Obj = */
3366 {
3367 /* .uVersion = */ RTVFSOBJOPS_VERSION,
3368 /* .enmType = */ RTVFSOBJTYPE_VFS,
3369 /* .pszName = */ "NtfsVol",
3370 /* .pfnClose = */ rtFsNtfsVol_Close,
3371 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
3372 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
3373 },
3374 /* .uVersion = */ RTVFSOPS_VERSION,
3375 /* .fFeatures = */ 0,
3376 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
3377 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
3378 /* .uEndMarker = */ RTVFSOPS_VERSION
3379};
3380
3381
3382/**
3383 * Checks that the storage for the given attribute is all marked allocated in
3384 * the allocation bitmap of the volume.
3385 *
3386 * @returns IPRT status code.
3387 * @param pThis The NTFS volume instance.
3388 * @param pAttr The attribute to check.
3389 * @param pszDesc Description of the attribute.
3390 * @param pErrInfo Where to return error details.
3391 */
3392static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo)
3393{
3394 PRTFSNTFSATTRSUBREC pSubRec = NULL;
3395 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
3396 uint64_t offFile = 0;
3397 for (;;)
3398 {
3399 uint32_t const cExtents = pTable->cExtents;
3400 PRTFSNTFSEXTENT paExtents = pTable->paExtents;
3401 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++)
3402 {
3403 uint64_t const off = paExtents[iExtent].off;
3404 if (off == UINT64_MAX)
3405 offFile += paExtents[iExtent].cbExtent;
3406 else
3407 {
3408 uint64_t iCluster = off >> pThis->cClusterShift;
3409 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift;
3410 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent);
3411 Assert(cClusters != 0);
3412
3413 while (cClusters-- > 0)
3414 {
3415 bool fState = false;
3416 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
3417 if (RT_FAILURE(rc))
3418 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc,
3419 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)",
3420 iCluster, pszDesc, offFile);
3421 if (!fState)
3422 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3423 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated",
3424 iCluster, offFile, pszDesc);
3425 offFile += pThis->cbCluster;
3426 }
3427 }
3428 }
3429
3430 /* Next table. */
3431 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead;
3432 if (!pSubRec)
3433 return VINF_SUCCESS;
3434 pTable = &pSubRec->Extents;
3435 }
3436}
3437
3438
3439/**
3440 * Loads, validates and setups the '.' (NTFS_MFT_IDX_ROOT) MFT entry.
3441 *
3442 * @returns IPRT status code
3443 * @param pThis The NTFS volume instance. Will set pawcUpcase.
3444 * @param pErrInfo Where to return additional error info.
3445 */
3446static int rtFsNtfsVolLoadRootDir(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3447{
3448 /*
3449 * Load it and do some checks.
3450 */
3451 PRTFSNTFSCORE pCore;
3452 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_ROOT, false /*fRelaxedUsa*/, &pCore, pErrInfo); // DON'T COMMIT
3453 if (RT_SUCCESS(rc))
3454 {
3455 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3456 if (!pFilenameAttr)
3457 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: has no FILENAME attribute!");
3458 else if (pFilenameAttr->pAttrHdr->fNonResident)
3459 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: FILENAME attribute is non-resident!");
3460 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[1]))
3461 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3462 "RootDir: FILENAME attribute value size is too small: %#x",
3463 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3464 else
3465 {
3466 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3467 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3468 if ( pFilename->cwcFilename != 1
3469 || ( RTUtf16NICmpAscii(pFilename->wszFilename, ".", 1) != 0
3470 && RTUtf16NICmpAscii(pFilename->wszFilename, "$", 1) != 0))
3471 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3472 "RootDir: FILENAME is not '.' nor '$: '%.*ls'",
3473 pFilename->cwcFilename, pFilename->wszFilename);
3474 else
3475 {
3476 PRTFSNTFSATTR pIndexRoot = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3477 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3478 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
3479 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3480 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
3481 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3482 if (!pIndexRoot)
3483 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ROOT attribute named $I30");
3484 else if (!pIndexAlloc && pIndexBitmap)
3485 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ALLOCATION attribute named $I30");
3486 else if (!pIndexBitmap && pIndexAlloc)
3487 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no BITMAP attribute named $I30");
3488 if (RT_SUCCESS(rc) && pIndexAlloc)
3489 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexAlloc, "RootDir", pErrInfo);
3490 if (RT_SUCCESS(rc) && pIndexBitmap)
3491 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexBitmap, "RootDir/bitmap", pErrInfo);
3492 if (RT_SUCCESS(rc))
3493 {
3494 /*
3495 * Load it as a normal directory.
3496 */
3497 PRTFSNTFSDIRSHRD pSharedDir;
3498 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, &pSharedDir, pErrInfo, "RootDir");
3499 if (RT_SUCCESS(rc))
3500 {
3501 rtFsNtfsCore_Release(pCore);
3502 pThis->pRootDir = pSharedDir;
3503 return VINF_SUCCESS;
3504 }
3505 }
3506 }
3507 }
3508 rtFsNtfsCore_Release(pCore);
3509 }
3510 else
3511 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Root dir: Error reading MFT record");
3512 return rc;
3513}
3514
3515
3516/**
3517 * Loads, validates and setups the '$UpCase' (NTFS_MFT_IDX_UP_CASE) MFT entry.
3518 *
3519 * This is needed for filename lookups, I think.
3520 *
3521 * @returns IPRT status code
3522 * @param pThis The NTFS volume instance. Will set pawcUpcase.
3523 * @param pErrInfo Where to return additional error info.
3524 */
3525static int rtFsNtfsVolLoadUpCase(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3526{
3527 PRTFSNTFSCORE pCore;
3528 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_UP_CASE, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3529 if (RT_SUCCESS(rc))
3530 {
3531 PRTFSNTFSATTR pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
3532 if (pDataAttr)
3533 {
3534 /*
3535 * Validate the '$Upcase' MFT record.
3536 */
3537 uint32_t const cbMin = 512;
3538 uint32_t const cbMax = _128K;
3539 if (!pDataAttr->pAttrHdr->fNonResident)
3540 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: unnamed DATA attribute is resident!");
3541 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) < cbMin
3542 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) > cbMax)
3543 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3544 "$UpCase: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX32",
3545 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated), cbMin, cbMax);
3546 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) < cbMin
3547 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
3548 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
3549 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) & 1) )
3550 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3551 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
3552 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData), cbMin,
3553 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
3554 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) < cbMin
3555 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized)
3556 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated)
3557 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) & 1) )
3558 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3559 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
3560 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized), cbMin,
3561 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
3562 else if (pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit != 0)
3563 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3564 "$UpCase: unnamed DATA attribute is compressed: %#x",
3565 pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit);
3566 else
3567 {
3568 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3569 if (!pFilenameAttr)
3570 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase has no FILENAME attribute!");
3571 else if (pFilenameAttr->pAttrHdr->fNonResident)
3572 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase FILENAME attribute is non-resident!");
3573 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3574 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3575 "$UpCase: FILENAME attribute value size is too small: %#x",
3576 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3577 else
3578 {
3579 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3580 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3581 if ( pFilename->cwcFilename != 7
3582 || RTUtf16NICmpAscii(pFilename->wszFilename, "$UpCase", 7) != 0)
3583 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3584 "$UpCase: FILENAME isn't '$UpCase': '%.*ls'",
3585 pFilename->cwcFilename, pFilename->wszFilename);
3586 else
3587 {
3588 /*
3589 * Allocate memory for the uppercase table and read it.
3590 */
3591 PRTUTF16 pawcUpcase;
3592 pThis->pawcUpcase = pawcUpcase = (PRTUTF16)RTMemAlloc(_64K * sizeof(pThis->pawcUpcase[0]));
3593 if (pawcUpcase)
3594 {
3595 for (size_t i = 0; i < _64K; i++)
3596 pawcUpcase[i] = (uint16_t)i;
3597
3598 rc = rtFsNtfsAttr_Read(pDataAttr, 0, pawcUpcase, pDataAttr->pAttrHdr->u.NonRes.cbData);
3599 if (RT_SUCCESS(rc))
3600 {
3601 /*
3602 * Check the data.
3603 */
3604 for (size_t i = 1; i < _64K; i++)
3605 if (pawcUpcase[i] == 0)
3606 {
3607 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3608 "$UpCase entry %#x is zero!", i);
3609 break;
3610 }
3611
3612 /*
3613 * While we still have the $UpCase file open, check it against the allocation bitmap.
3614 */
3615 if (RT_SUCCESS(rc))
3616 rc = rtFsNtfsVolCheckBitmap(pThis, pDataAttr, "$UpCase", pErrInfo);
3617
3618 /* We're done, no need for special success return here though. */
3619 }
3620 else
3621 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, "Error reading $UpCase data into memory");
3622 }
3623 else
3624 rc = VERR_NO_MEMORY;
3625 }
3626 }
3627 }
3628 }
3629 else
3630 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: has no unnamed DATA attribute!");
3631 rtFsNtfsCore_Release(pCore);
3632 }
3633 else
3634 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$UpCase: Error reading the MFT record");
3635 return rc;
3636}
3637
3638
3639/**
3640 * Loads the allocation bitmap and does basic validation of.
3641 *
3642 * @returns IPRT status code.
3643 * @param pThis The NTFS volume instance. Will set up the
3644 * 'Allocation bitmap and cache' fields.
3645 * @param pErrInfo Where to return error details.
3646 */
3647static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3648{
3649 PRTFSNTFSCORE pCore;
3650 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3651 if (RT_SUCCESS(rc))
3652 {
3653 PRTFSNTFSATTR pMftBitmap;
3654 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
3655 if (pMftBitmap)
3656 {
3657 /*
3658 * Validate the '$Bitmap' MFT record.
3659 * We expect the bitmap to be fully initialized and be sized according to the
3660 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size.
3661 */
3662 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8);
3663 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster);
3664 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8);
3665 if (!pMftBitmap->pAttrHdr->fNonResident)
3666 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!");
3667 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap
3668 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap)
3669 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3670 "$Bitmap: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3671 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap);
3672 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap
3673 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)
3674 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData))
3675 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3676 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3677 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap,
3678 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
3679 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap
3680 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized)
3681 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated))
3682 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3683 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
3684 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap,
3685 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
3686 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0)
3687 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3688 "$Bitmap: unnamed DATA attribute is compressed: %#x",
3689 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit);
3690 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */
3691 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3692 "$Bitmap: unnamed DATA attribute is expected to have a single extent: %u extents",
3693 pMftBitmap->Extents.cExtents);
3694 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX)
3695 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse");
3696 else
3697 {
3698 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3699 if (!pFilenameAttr)
3700 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!");
3701 else if (pFilenameAttr->pAttrHdr->fNonResident)
3702 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!");
3703 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3704 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3705 "$Bitmap FILENAME attribute value size is too small: %#x",
3706 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3707 else
3708 {
3709 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3710 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3711 if ( pFilename->cwcFilename != 7
3712 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0)
3713 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3714 "$Bitmap: FILENAME isn't '$Bitmap': '%.*ls'",
3715 pFilename->cwcFilename, pFilename->wszFilename);
3716 else
3717 {
3718 /*
3719 * Read some of it into the buffer and check that essential stuff is flagged as allocated.
3720 */
3721 /* The boot sector. */
3722 bool fState = false;
3723 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState);
3724 if (RT_SUCCESS(rc) && !fState)
3725 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3726 "MFT allocation bitmap error: Bootsector isn't marked allocated!");
3727 else if (RT_FAILURE(rc))
3728 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3729 "MFT allocation bitmap (offset 0) read error: %Rrc", rc);
3730
3731 /* The bitmap ifself, the MFT data, and the MFT bitmap. */
3732 if (RT_SUCCESS(rc))
3733 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo);
3734 if (RT_SUCCESS(rc))
3735 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo);
3736 if (RT_SUCCESS(rc))
3737 rc = rtFsNtfsVolCheckBitmap(pThis,
3738 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP),
3739 "MFT Bitmap", pErrInfo);
3740 if (RT_SUCCESS(rc))
3741 {
3742 /*
3743 * Looks like the bitmap is good.
3744 */
3745 return VINF_SUCCESS;
3746 }
3747 }
3748 }
3749 }
3750 pThis->pMftBitmap = NULL;
3751 }
3752 else
3753 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Bitmap: has no unnamed DATA attribute!");
3754 rtFsNtfsCore_Release(pCore);
3755 }
3756 else
3757 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$Bitmap: Error MFT record");
3758 return rc;
3759}
3760
3761
3762/**
3763 * Loads, validates and setups the '$Volume' (NTFS_MFT_IDX_VOLUME) MFT entry.
3764 *
3765 * @returns IPRT status code
3766 * @param pThis The NTFS volume instance. Will set uNtfsVersion
3767 * and fVolumeFlags.
3768 * @param pErrInfo Where to return additional error info.
3769 */
3770static int rtFsNtfsVolLoadVolumeInfo(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3771{
3772 PRTFSNTFSCORE pCore;
3773 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_VOLUME, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3774 if (RT_SUCCESS(rc))
3775 {
3776 PRTFSNTFSATTR pVolInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_VOLUME_INFORMATION);
3777 if (pVolInfoAttr)
3778 {
3779 /*
3780 * Validate the '$Volume' MFT record.
3781 */
3782 if (pVolInfoAttr->pAttrHdr->fNonResident)
3783 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume unnamed VOLUME_INFORMATION attribute is not resident!");
3784 else if ( pVolInfoAttr->cbResident != sizeof(NTFSATVOLUMEINFO)
3785 || pVolInfoAttr->cbValue != sizeof(NTFSATVOLUMEINFO))
3786 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3787 "$Volume VOLUME_INFORMATION attribute has the wrong size: cbValue=%#RX64, cbResident=%#RX32, expected %#x\n",
3788 pVolInfoAttr->cbValue, pVolInfoAttr->cbResident, sizeof(NTFSATVOLUMEINFO));
3789 else
3790 {
3791 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
3792 if (!pFilenameAttr)
3793 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume has no FILENAME attribute!");
3794 else if (pFilenameAttr->pAttrHdr->fNonResident)
3795 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume FILENAME attribute is non-resident!");
3796 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
3797 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3798 "$Volume FILENAME attribute value size is too small: %#x",
3799 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3800 else
3801 {
3802 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3803 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3804 if ( pFilename->cwcFilename != 7
3805 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Volume", 7) != 0)
3806 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3807 "$Volume FILENAME isn't '$Volume': '%.*ls'",
3808 pFilename->cwcFilename, pFilename->wszFilename);
3809 else
3810 {
3811 /*
3812 * Look at the information.
3813 */
3814 PCNTFSATVOLUMEINFO pVolInfo;
3815 pVolInfo = (PCNTFSATVOLUMEINFO)((uint8_t *)pVolInfoAttr->pAttrHdr + pVolInfoAttr->pAttrHdr->u.Res.offValue);
3816 pThis->uNtfsVersion = RTFSNTFS_MAKE_VERSION(pVolInfo->uMajorVersion, pVolInfo->uMinorVersion);
3817 pThis->fVolumeFlags = RT_LE2H_U16(pVolInfo->fFlags);
3818 Log(("NTFS: Version %u.%u, flags=%#x\n", pVolInfo->uMajorVersion, pVolInfo->uMinorVersion, pThis->fVolumeFlags));
3819
3820 /* We're done, no need for special success return here though. */
3821 }
3822 }
3823 }
3824 }
3825 else
3826 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3827 "MFT record $Volume has no unnamed VOLUME_INFORMATION attribute!");
3828 rtFsNtfsCore_Release(pCore);
3829 }
3830 else
3831 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading $Volume MFT record");
3832 return rc;
3833}
3834
3835
3836/**
3837 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry.
3838 *
3839 * This is the first thing we do after we've checked out the boot sector and
3840 * extracted information from it, since everything else depends on us being able
3841 * to access the MFT data.
3842 *
3843 * @returns IPRT status code
3844 * @param pThis The NTFS volume instance. Will set pMftData.
3845 * @param pErrInfo Where to return additional error info.
3846 */
3847static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
3848{
3849 /*
3850 * Bootstrap the MFT data stream.
3851 */
3852 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT);
3853 AssertReturn(pRec, VERR_NO_MEMORY);
3854
3855#if 0 && defined(LOG_ENABLED)
3856 for (uint32_t i = 0; i < 128; i++)
3857 {
3858 uint64_t const offDisk = (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord;
3859 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
3860 if (RT_SUCCESS(rc))
3861 {
3862 pRec->TreeNode.Key = i;
3863 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
3864 pRec->TreeNode.Key = 0;
3865 }
3866 }
3867#endif
3868
3869 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift;
3870 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
3871 if (RT_SUCCESS(rc))
3872 {
3873 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, true, pErrInfo);
3874 if (RT_SUCCESS(rc))
3875 {
3876#ifdef LOG_ENABLED
3877 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
3878#endif
3879 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
3880 }
3881 if (RT_SUCCESS(rc))
3882 {
3883 pThis->pMftData = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_DATA);
3884 if (pThis->pMftData)
3885 {
3886 /*
3887 * Validate the '$Mft' MFT record.
3888 */
3889 if (!pThis->pMftData->pAttrHdr->fNonResident)
3890 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!");
3891 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U
3892 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking)
3893 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3894 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
3895 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated));
3896 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U
3897 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking)
3898 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3899 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64",
3900 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized));
3901 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U
3902 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking)
3903 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3904 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
3905 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData));
3906 else if (pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit != 0)
3907 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3908 "MFT record #0 unnamed DATA attribute is compressed: %#x",
3909 pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit);
3910 else if (pThis->pMftData->Extents.cExtents == 0)
3911 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3912 "MFT record #0 unnamed DATA attribute has no data on the disk");
3913 else if (pThis->pMftData->Extents.paExtents[0].off != offDisk)
3914 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3915 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64",
3916 pThis->pMftData->Extents.paExtents[0].off, offDisk);
3917 else if (!rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_BITMAP))
3918 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!");
3919 else
3920 {
3921 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_FILENAME);
3922 if (!pFilenameAttr)
3923 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!");
3924 else if (pFilenameAttr->pAttrHdr->fNonResident)
3925 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!");
3926 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4]))
3927 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3928 "MFT record #0 FILENAME attribute value size is too small: %#x",
3929 pFilenameAttr->pAttrHdr->u.Res.cbValue);
3930 else
3931 {
3932 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
3933 + pFilenameAttr->pAttrHdr->u.Res.offValue);
3934 if ( pFilename->cwcFilename != 4
3935 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0)
3936 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3937 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'",
3938 pFilename->cwcFilename, pFilename->wszFilename);
3939 else
3940 {
3941 /*
3942 * Looks like we're good.
3943 */
3944 return VINF_SUCCESS;
3945 }
3946 }
3947 }
3948 pThis->pMftData = NULL;
3949 }
3950 else
3951 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!");
3952 }
3953 rtFsNtfsCore_Release(pRec->pCore);
3954 rtFsNtfsMftRec_Release(pRec, pThis);
3955 }
3956 else
3957 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0");
3958 return rc;
3959}
3960
3961
3962/**
3963 * Loads the bootsector and parses it, copying values into the instance data.
3964 *
3965 * @returns IRPT status code.
3966 * @param pThis The instance data.
3967 * @param pvBuf The buffer.
3968 * @param cbBuf The buffer size.
3969 * @param pErrInfo Where to return additional error details.
3970 */
3971static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
3972{
3973 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
3974
3975 /*
3976 * Read the boot sector and check that it makes sense for a NTFS volume.
3977 *
3978 * Note! There are two potential backup locations of the boot sector, however we
3979 * currently don't implement falling back on these on corruption/read errors.
3980 */
3981 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
3982 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
3983 if (RT_FAILURE(rc))
3984 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
3985
3986 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
3987 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
3988 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
3989
3990 /* Check must-be-zero BPB fields. */
3991 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
3992 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
3993 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
3994 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
3995 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
3996 pBootSector->Bpb.Ntfs.Bpb.cFats);
3997 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
3998 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
3999 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
4000 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
4001 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
4002 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
4003 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
4004 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
4005 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
4006
4007 /* Check other relevant BPB fields. */
4008 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
4009 if ( cbSector != 512
4010 && cbSector != 1024
4011 && cbSector != 2048
4012 && cbSector != 4096)
4013 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
4014 pThis->cbSector = cbSector;
4015 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
4016
4017 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
4018 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
4019 || cClusterPerSector == 0)
4020 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
4021 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
4022
4023 pThis->cbCluster = cClusterPerSector * cbSector;
4024 if (pThis->cbCluster > _64K)
4025 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4026 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
4027 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
4028 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
4029 pThis->iMaxVirtualCluster = (uint64_t)INT64_MAX >> pThis->cClusterShift;
4030 Log2(("NTFS BPB: iMaxVirtualCluster=%#RX64\n", pThis->iMaxVirtualCluster));
4031
4032 /* NTFS BPB: cSectors. */
4033 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
4034 if (cSectors > pThis->cbBacking / pThis->cbSector)
4035 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4036 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
4037 cSectors, pThis->cbBacking / pThis->cbSector);
4038 if (cSectors < 256)
4039 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
4040 pThis->cbVolume = cSectors * pThis->cbSector;
4041 pThis->cClusters = cSectors / cClusterPerSector;
4042 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
4043 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
4044
4045 /* NTFS BPB: MFT location. */
4046 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
4047 if ( uLcn < 1
4048 || uLcn >= pThis->cClusters)
4049 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4050 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
4051 pThis->uLcnMft = uLcn;
4052 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
4053
4054 /* NTFS BPB: Mirror MFT location. */
4055 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
4056 if ( uLcn < 1
4057 || uLcn >= pThis->cClusters)
4058 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4059 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
4060 pThis->uLcnMftMirror = uLcn;
4061 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
4062
4063 /* NTFS BPB: Size of MFT file record. */
4064 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
4065 {
4066 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
4067 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
4068 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4069 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
4070 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
4071 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
4072 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
4073 }
4074 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
4075 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
4076 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4077 "NTFS clusters-per-mft-record is out of shift range: %d",
4078 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
4079 else
4080 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
4081 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
4082 if ( pThis->cbMftRecord > _32K
4083 || pThis->cbMftRecord < 256)
4084 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4085 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
4086
4087 /* NTFS BPB: Default index node size */
4088 if (pBootSector->Bpb.Ntfs.cClustersPerIndexNode >= 0)
4089 {
4090 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode)
4091 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode == 0)
4092 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4093 "NTFS default clusters-per-index-tree-node is zero or not a power of two: %#x",
4094 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
4095 pThis->cbDefaultIndexNode = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode << pThis->cClusterShift;
4096 Assert(pThis->cbDefaultIndexNode == pBootSector->Bpb.Ntfs.cClustersPerIndexNode * pThis->cbCluster);
4097 }
4098 else if ( pBootSector->Bpb.Ntfs.cClustersPerIndexNode < -32
4099 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode > -9)
4100 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
4101 "NTFS default clusters-per-index-tree-node is out of shift range: %d",
4102 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
4103 else
4104 pThis->cbDefaultIndexNode = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
4105 Log2(("NTFS BPB: cbDefaultIndexNode=%#x\n", pThis->cbDefaultIndexNode));
4106
4107 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
4108 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
4109
4110
4111 return VINF_SUCCESS;
4112}
4113
4114
4115RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
4116{
4117 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
4118 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
4119 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
4120
4121 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
4122 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
4123
4124 /*
4125 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
4126 */
4127 RTVFS hVfs;
4128 PRTFSNTFSVOL pThis;
4129 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
4130 if (RT_SUCCESS(rc))
4131 {
4132 pThis->hVfsBacking = hVfsFileIn;
4133 pThis->hVfsSelf = hVfs;
4134 pThis->fMntFlags = fMntFlags;
4135 pThis->fNtfsFlags = fNtfsFlags;
4136 RTListInit(&pThis->IdxNodeUnusedHead);
4137
4138 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
4139 if (RT_SUCCESS(rc))
4140 {
4141 void *pvBuf = RTMemTmpAlloc(_64K);
4142 if (pvBuf)
4143 {
4144 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
4145 if (RT_SUCCESS(rc))
4146 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo);
4147 if (RT_SUCCESS(rc))
4148 rc = rtFsNtfsVolLoadVolumeInfo(pThis, pErrInfo);
4149 if (RT_SUCCESS(rc))
4150 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo);
4151 if (RT_SUCCESS(rc))
4152 rc = rtFsNtfsVolLoadUpCase(pThis, pErrInfo);
4153 if (RT_SUCCESS(rc))
4154 rc = rtFsNtfsVolLoadRootDir(pThis, pErrInfo);
4155 RTMemTmpFree(pvBuf);
4156 if (RT_SUCCESS(rc))
4157 {
4158 *phVfs = hVfs;
4159 return VINF_SUCCESS;
4160 }
4161 }
4162 else
4163 rc = VERR_NO_TMP_MEMORY;
4164 }
4165
4166 RTVfsRelease(hVfs);
4167 *phVfs = NIL_RTVFS;
4168 }
4169 else
4170 RTVfsFileRelease(hVfsFileIn);
4171
4172 return rc;
4173}
4174
4175
4176/**
4177 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
4178 */
4179static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
4180 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
4181{
4182 RT_NOREF(pProviderReg);
4183
4184 /*
4185 * Basic checks.
4186 */
4187 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
4188 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
4189 if ( pElement->enmType != RTVFSOBJTYPE_VFS
4190 && pElement->enmType != RTVFSOBJTYPE_DIR)
4191 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
4192 if (pElement->cArgs > 1)
4193 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
4194
4195 /*
4196 * Parse the flag if present, save in pElement->uProvider.
4197 */
4198 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
4199 if (pElement->cArgs > 0)
4200 {
4201 const char *psz = pElement->paArgs[0].psz;
4202 if (*psz)
4203 {
4204 if (!strcmp(psz, "ro"))
4205 fReadOnly = true;
4206 else if (!strcmp(psz, "rw"))
4207 fReadOnly = false;
4208 else
4209 {
4210 *poffError = pElement->paArgs[0].offSpec;
4211 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
4212 }
4213 }
4214 }
4215
4216 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
4217 return VINF_SUCCESS;
4218}
4219
4220
4221/**
4222 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
4223 */
4224static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
4225 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
4226 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
4227{
4228 RT_NOREF(pProviderReg, pSpec, poffError);
4229
4230 int rc;
4231 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
4232 if (hVfsFileIn != NIL_RTVFSFILE)
4233 {
4234 RTVFS hVfs;
4235 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
4236 RTVfsFileRelease(hVfsFileIn);
4237 if (RT_SUCCESS(rc))
4238 {
4239 *phVfsObj = RTVfsObjFromVfs(hVfs);
4240 RTVfsRelease(hVfs);
4241 if (*phVfsObj != NIL_RTVFSOBJ)
4242 return VINF_SUCCESS;
4243 rc = VERR_VFS_CHAIN_CAST_FAILED;
4244 }
4245 }
4246 else
4247 rc = VERR_VFS_CHAIN_CAST_FAILED;
4248 return rc;
4249}
4250
4251
4252/**
4253 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
4254 */
4255static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
4256 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
4257 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
4258{
4259 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
4260 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
4261 || !pReuseElement->paArgs[0].uProvider)
4262 return true;
4263 return false;
4264}
4265
4266
4267/** VFS chain element 'file'. */
4268static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
4269{
4270 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
4271 /* fReserved = */ 0,
4272 /* pszName = */ "ntfs",
4273 /* ListEntry = */ { NULL, NULL },
4274 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
4275 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
4276 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
4277 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
4278 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
4279 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
4280};
4281
4282RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
4283
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette