VirtualBox

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

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

IPRT/ntfsvfs.cpp: More on the subject of directories (indexes). [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 136.3 KB
Line 
1/* $Id: ntfsvfs.cpp 69913 2017-12-03 19:09:15Z 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 cache size. */
51#define RTFSNTFS_MAX_BITMAP_CACHE _64K
52
53/** Makes a combined NTFS version value.
54 * @see RTFSNTFSVOL::uNtfsVersion */
55#define RTFSNTFS_MAKE_VERSION(a_uMajor, a_uMinor) RT_MAKE_U16(a_uMinor, a_uMajor)
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61/** Pointer to the instance data for a NTFS volume. */
62typedef struct RTFSNTFSVOL *PRTFSNTFSVOL;
63/** Pointer to a NTFS MFT record. */
64typedef struct RTFSNTFSMFTREC *PRTFSNTFSMFTREC;
65/** Poitner to a NTFS core object record. */
66typedef struct RTFSNTFSCORE *PRTFSNTFSCORE;
67
68
69/**
70 * NTFS disk allocation extent (internal representation).
71 */
72typedef struct RTFSNTFSEXTENT
73{
74 /** The disk or partition byte offset.
75 * This is set to UINT64_MAX for parts of sparse files that aren't recorded. */
76 uint64_t off;
77 /** The size of the extent in bytes. */
78 uint64_t cbExtent;
79} RTFSNTFSEXTENT;
80/** Pointer to an NTFS 9660 extent. */
81typedef RTFSNTFSEXTENT *PRTFSNTFSEXTENT;
82/** Pointer to a const NTFS 9660 extent. */
83typedef RTFSNTFSEXTENT const *PCRTFSNTFSEXTENT;
84
85/**
86 * An array of zero or more extents.
87 */
88typedef struct RTFSNTFSEXTENTS
89{
90 /** Number of bytes covered by the extents. */
91 uint64_t cbData;
92 /** Number of allocation extents. */
93 uint32_t cExtents;
94 /** Array of allocation extents. */
95 PRTFSNTFSEXTENT paExtents;
96} RTFSNTFSEXTENTS;
97/** Pointer to an extent array. */
98typedef RTFSNTFSEXTENTS *PRTFSNTFSEXTENTS;
99/** Pointer to a const extent array. */
100typedef RTFSNTFSEXTENTS const *PCRTFSNTFSEXTENTS;
101
102
103/**
104 * NTFS MFT record.
105 *
106 * These are kept in a tree to , so
107 */
108typedef struct RTFSNTFSMFTREC
109{
110 /** MFT record number (index) as key. */
111 AVLU64NODECORE TreeNode;
112 /** Pointer to the next MFT record if chained. Holds a reference. */
113 PRTFSNTFSMFTREC pNext;
114 union
115 {
116 /** Generic record pointer. RTFSNTFSVOL::cbMftRecord in size. */
117 uint8_t *pbRec;
118 /** Pointer to the file record. */
119 PNTFSRECFILE pFileRec;
120 } RT_UNION_NM(u);
121 /** Pointer to the core object with the parsed data.
122 * This is a weak reference. Non-base MFT record all point to the base one. */
123 PRTFSNTFSCORE pCore;
124 /** Reference counter. */
125 uint32_t volatile cRefs;
126 /** Set if this is a base MFT record. */
127 bool fIsBase;
128} RTFSNTFSMFTREC;
129
130
131/** Pointer to a attribute subrecord structure. */
132typedef struct RTFSNTFSATTRSUBREC *PRTFSNTFSATTRSUBREC;
133
134/**
135 * An attribute subrecord.
136 *
137 * This is for covering non-resident attributes that have had their allocation
138 * list split.
139 */
140typedef struct RTFSNTFSATTRSUBREC
141{
142 /** Pointer to the next one. */
143 PRTFSNTFSATTRSUBREC pNext;
144 /** Pointer to the attribute header.
145 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
146 PNTFSATTRIBHDR pAttrHdr;
147 /** Disk space allocation if non-resident. */
148 RTFSNTFSEXTENTS Extents;
149} RTFSNTFSATTRSUBREC;
150
151/**
152 * An attribute.
153 */
154typedef struct RTFSNTFSATTR
155{
156 /** List entry (head RTFSNTFSCORE::AttribHead). */
157 RTLISTNODE ListEntry;
158 /** Pointer to the core object this attribute belongs to. */
159 PRTFSNTFSCORE pCore;
160 /** Pointer to the attribute header.
161 * The MFT is held down by RTFSNTFSCORE via pMftEntry. */
162 PNTFSATTRIBHDR pAttrHdr;
163 /** The offset of the attribute header in the MFT record.
164 * This is needed to validate header relative offsets. */
165 uint32_t offAttrHdrInMftRec;
166 /** Number of resident bytes available (can be smaller than cbValue).
167 * Set to zero for non-resident attributes. */
168 uint32_t cbResident;
169 /** The (uncompressed) attribute size. */
170 uint64_t cbValue;
171 /** Disk space allocation if non-resident. */
172 RTFSNTFSEXTENTS Extents;
173 /** Pointer to any subrecords containing further allocation extents. */
174 PRTFSNTFSATTRSUBREC pSubRecHead;
175} RTFSNTFSATTR;
176/** Pointer to a attribute structure. */
177typedef RTFSNTFSATTR *PRTFSNTFSATTR;
178
179
180/**
181 * NTFS file system object, shared part.
182 */
183typedef struct RTFSNTFSCORE
184{
185 /** Reference counter. */
186 uint32_t volatile cRefs;
187 /** Pointer to the volume. */
188 PRTFSNTFSVOL pVol;
189 /** Pointer to the head of the MFT record chain for this object.
190 * Holds a reference. */
191 PRTFSNTFSMFTREC pMftRec;
192 /** List of attributes (RTFSNTFSATTR). */
193 RTLISTANCHOR AttribHead;
194} RTFSNTFSCORE;
195
196/**
197 * Pointer to a shared NTFS directory object.
198 */
199typedef struct RTFSNTFSDIRSHRD
200{
201 /** Reference counter. */
202 uint32_t volatile cRefs;
203
204 /** Pointer to the core object for the directory (referenced). */
205 PRTFSNTFSCORE pCore;
206 /** Pointer to the index root attribute. */
207 PRTFSNTFSATTR pIndexRoot;
208
209 /** Pointer to the index allocation attribute, if present.
210 * This and the bitmap may be absent if the whole directory fits into the
211 * root index. */
212 PRTFSNTFSATTR pIndexAlloc;
213 /** Pointer to the index allocation bitmap attribute, if present. */
214 PRTFSNTFSATTR pIndexBitmap;
215
216} RTFSNTFSDIRSHRD;
217/** Pointer to a shared NTFS directory object. */
218typedef RTFSNTFSDIRSHRD *PRTFSNTFSDIRSHRD;
219
220
221/**
222 * Instance data for an NTFS volume.
223 */
224typedef struct RTFSNTFSVOL
225{
226 /** Handle to itself. */
227 RTVFS hVfsSelf;
228 /** The file, partition, or whatever backing the NTFS volume. */
229 RTVFSFILE hVfsBacking;
230 /** The size of the backing thingy. */
231 uint64_t cbBacking;
232 /** The formatted size of the volume. */
233 uint64_t cbVolume;
234 /** cbVolume expressed as a cluster count. */
235 uint64_t cClusters;
236
237 /** RTVFSMNT_F_XXX. */
238 uint32_t fMntFlags;
239 /** RTFSNTVFS_F_XXX (currently none defined). */
240 uint32_t fNtfsFlags;
241
242 /** The (logical) sector size. */
243 uint32_t cbSector;
244
245 /** The (logical) cluster size. */
246 uint32_t cbCluster;
247 /** Max cluster count value that won't overflow a signed 64-bit when
248 * converted to bytes. Inclusive. */
249 uint64_t iMaxVirtualCluster;
250 /** The shift count for converting between bytes and clusters. */
251 uint8_t cClusterShift;
252
253 /** Explicit padding. */
254 uint8_t abReserved[3];
255 /** The NTFS version of the volume (RTFSNTFS_MAKE_VERSION). */
256 uint16_t uNtfsVersion;
257 /** The NTFS_VOLUME_F_XXX. */
258 uint16_t fVolumeFlags;
259
260 /** The logical cluster number of the MFT. */
261 uint64_t uLcnMft;
262 /** The logical cluster number of the mirror MFT. */
263 uint64_t uLcnMftMirror;
264
265 /** The MFT record size. */
266 uint32_t cbMftRecord;
267 /** The default index (B-tree) node size. */
268 uint32_t cbDefaultIndexNode;
269
270 /** The volume serial number. */
271 uint64_t uSerialNo;
272
273 /** The '$Mft' data attribute. */
274 PRTFSNTFSATTR pMftData;
275
276 /** The root directory. */
277 PRTFSNTFSDIRSHRD pRootDir;
278
279 /** @name Allocation bitmap and cache.
280 * @{ */
281 /** The '$Bitmap' data attribute. */
282 PRTFSNTFSATTR pMftBitmap;
283 /** The first cluster currently loaded into the bitmap cache . */
284 uint64_t iFirstBitmapCluster;
285 /** The number of clusters currently loaded into the bitmap cache */
286 uint32_t cBitmapClusters;
287 /** The size of the pvBitmap allocation. */
288 uint32_t cbBitmapAlloc;
289 /** Allocation bitmap cache buffer. */
290 void *pvBitmap;
291 /** @} */
292
293 /** Lower to uppercase conversion table for this filesystem.
294 * This always has 64K valid entries. */
295 PRTUTF16 pawcUpcase;
296
297 /** Root of the MFT record tree (RTFSNTFSMFTREC). */
298 AVLU64TREE MftRoot;
299
300} RTFSNTFSVOL;
301
302
303
304/*********************************************************************************************************************************
305* Internal Functions *
306*********************************************************************************************************************************/
307static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis);
308#ifdef LOG_ENABLED
309static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot);
310#endif
311
312
313
314/**
315 * Checks if a bit is set in an NTFS bitmap (little endian).
316 *
317 * @returns true if set, false if not.
318 * @param pvBitmap The bitmap buffer.
319 * @param iBit The bit.
320 */
321DECLINLINE(bool) rtFsNtfsBitmap_IsSet(void *pvBitmap, uint32_t iBit)
322{
323#if 0 //def RT_LITTLE_ENDIAN
324 return ASMBitTest(pvBitmap, iBit);
325#else
326 uint8_t b = ((uint8_t const *)pvBitmap)[iBit >> 3];
327 return RT_BOOL(b & (1 << (iBit & 7)));
328#endif
329}
330
331
332
333static PRTFSNTFSMFTREC rtFsNtfsVol_NewMftRec(PRTFSNTFSVOL pVol, uint64_t idMft)
334{
335 PRTFSNTFSMFTREC pRec = (PRTFSNTFSMFTREC)RTMemAllocZ(sizeof(*pRec));
336 if (pRec)
337 {
338 pRec->pbRec = (uint8_t *)RTMemAllocZ(pVol->cbMftRecord);
339 if (pRec->pbRec)
340 {
341 pRec->TreeNode.Key = idMft;
342 pRec->pNext = NULL;
343 pRec->cRefs = 1;
344 if (RTAvlU64Insert(&pVol->MftRoot, &pRec->TreeNode))
345 return pRec;
346 RTMemFree(pRec);
347 }
348 }
349 return NULL;
350}
351
352
353static uint32_t rtFsNtfsMftRec_Destroy(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
354{
355 RTMemFree(pThis->pbRec);
356 pThis->pbRec = NULL;
357
358 PAVLU64NODECORE pRemoved = RTAvlU64Remove(&pVol->MftRoot, pThis->TreeNode.Key);
359 Assert(pRemoved == &pThis->TreeNode); NOREF(pRemoved);
360
361 RTMemFree(pThis);
362
363 return 0;
364}
365
366
367static uint32_t rtFsNtfsMftRec_Retain(PRTFSNTFSMFTREC pThis)
368{
369 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
370 Assert(cRefs < 64);
371 return cRefs;
372}
373
374static uint32_t rtFsNtfsMftRec_Release(PRTFSNTFSMFTREC pThis, PRTFSNTFSVOL pVol)
375{
376 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
377 Assert(cRefs < 64);
378 if (cRefs != 0)
379 return cRefs;
380 return rtFsNtfsMftRec_Destroy(pThis, pVol);
381}
382
383
384#ifdef LOG_ENABLED
385/**
386 * Logs the MFT record
387 *
388 * @param pRec The MFT record to log.
389 * @param cbMftRecord MFT record size (from RTFSNTFSVOL).
390 */
391static void rtfsNtfsMftRec_Log(PRTFSNTFSMFTREC pRec, uint32_t cbMftRecord)
392{
393 if (LogIs2Enabled())
394 {
395 PCNTFSRECFILE pFileRec = pRec->pFileRec;
396 Log2(("NTFS: MFT #%#RX64\n", pRec->TreeNode.Key));
397 if (pFileRec->Hdr.uMagic == NTFSREC_MAGIC_FILE)
398 {
399 size_t const cbRec = cbMftRecord;
400 uint8_t const * const pbRec = pRec->pbRec;
401
402 Log2(("NTFS: FILE record: \n"));
403 Log2(("NTFS: UpdateSeqArray %#x L %#x\n", RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray), RT_LE2H_U16(pFileRec->Hdr.cUpdateSeqEntries) ));
404 Log2(("NTFS: uLsn %#RX64\n", RT_LE2H_U64(pFileRec->uLsn)));
405 Log2(("NTFS: uRecReuseSeqNo %#RX16\n", RT_LE2H_U16(pFileRec->uRecReuseSeqNo)));
406 Log2(("NTFS: cLinks %#RX16\n", RT_LE2H_U16(pFileRec->cLinks)));
407 Log2(("NTFS: offFirstAttrib %#RX16\n", RT_LE2H_U16(pFileRec->offFirstAttrib)));
408 Log2(("NTFS: fFlags %#RX16%s%s\n", RT_LE2H_U16(pFileRec->fFlags),
409 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_IN_USE ? " in-use" : "",
410 RT_LE2H_U16(pFileRec->fFlags) & NTFSRECFILE_F_DIRECTORY ? " directory" : ""));
411 Log2(("NTFS: cbRecUsed %#RX32\n", RT_LE2H_U32(pFileRec->cbRecUsed)));
412 Log2(("NTFS: BaseMftRec %#RX64, sqn %#x\n",
413 NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec), NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec)));
414 Log2(("NTFS: idNextAttrib %#RX16\n", RT_LE2H_U16(pFileRec->idNextAttrib)));
415 if ( RT_LE2H_U16(pFileRec->offFirstAttrib) >= sizeof(*pFileRec)
416 && ( RT_LE2H_U16(pFileRec->Hdr.offUpdateSeqArray) >= sizeof(*pFileRec)
417 || pFileRec->Hdr.offUpdateSeqArray == 0))
418 {
419 Log2(("NTFS: uPaddingOrUsa %#RX16\n", pFileRec->uPaddingOrUsa));
420 Log2(("NTFS: idxMftSelf %#RX32\n", RT_LE2H_U32(pFileRec->idxMftSelf)));
421 }
422
423 uint32_t offRec = pFileRec->offFirstAttrib;
424 size_t cbRecUsed = RT_MIN(cbRec, pFileRec->cbRecUsed);
425 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
426 {
427 PCNTFSATTRIBHDR pHdr = (PCNTFSATTRIBHDR)&pbRec[offRec];
428 uint32_t const cbAttrib = RT_LE2H_U32(pHdr->cbAttrib);
429 Log2(("NTFS: @%#05x: Attrib record: %#x LB %#x, instance #%#x, fFlags=%#RX16, %s\n", offRec,
430 RT_LE2H_U32(pHdr->uAttrType), cbAttrib, RT_LE2H_U16(pHdr->idAttrib), RT_LE2H_U16(pHdr->fFlags),
431 pHdr->fNonResident == 0 ? "resident" : pHdr->fNonResident == 1 ? "non-resident" : "bad-resident-flag"));
432 if (pHdr->offName && pHdr->cwcName)
433 {
434 if (offRec + RT_LE2H_U16(pHdr->offName) + pHdr->cwcName * sizeof(RTUTF16) <= cbRec)
435 Log2(("NTFS: Name %.*ls\n", pHdr->cwcName,&pbRec[offRec + RT_LE2H_U16(pHdr->offName)]));
436 else
437 Log2(("NTFS: Name <!out of bounds!> %#x L %#x\n", RT_LE2H_U16(pHdr->offName), pHdr->cwcName));
438 }
439 switch (pHdr->uAttrType)
440 {
441 case NTFS_AT_UNUSED: Log2(("NTFS: Type: UNUSED\n")); break;
442 case NTFS_AT_STANDARD_INFORMATION: Log2(("NTFS: Type: STANDARD_INFORMATION\n")); break;
443 case NTFS_AT_ATTRIBUTE_LIST: Log2(("NTFS: Type: ATTRIBUTE_LIST\n")); break;
444 case NTFS_AT_FILENAME: Log2(("NTFS: Type: FILENAME\n")); break;
445 case NTFS_AT_OBJECT_ID: Log2(("NTFS: Type: OBJECT_ID\n")); break;
446 case NTFS_AT_SECURITY_DESCRIPTOR: Log2(("NTFS: Type: SECURITY_DESCRIPTOR\n")); break;
447 case NTFS_AT_VOLUME_NAME: Log2(("NTFS: Type: VOLUME_NAME\n")); break;
448 case NTFS_AT_VOLUME_INFORMATION: Log2(("NTFS: Type: VOLUME_INFORMATION\n")); break;
449 case NTFS_AT_DATA: Log2(("NTFS: Type: DATA\n")); break;
450 case NTFS_AT_INDEX_ROOT: Log2(("NTFS: Type: INDEX_ROOT\n")); break;
451 case NTFS_AT_INDEX_ALLOCATION: Log2(("NTFS: Type: INDEX_ALLOCATION\n")); break;
452 case NTFS_AT_BITMAP: Log2(("NTFS: Type: BITMAP\n")); break;
453 case NTFS_AT_REPARSE_POINT: Log2(("NTFS: Type: REPARSE_POINT\n")); break;
454 case NTFS_AT_EA_INFORMATION: Log2(("NTFS: Type: EA_INFORMATION\n")); break;
455 case NTFS_AT_EA: Log2(("NTFS: Type: EA\n")); break;
456 case NTFS_AT_PROPERTY_SET: Log2(("NTFS: Type: PROPERTY_SET\n")); break;
457 case NTFS_AT_LOGGED_UTILITY_STREAM: Log2(("NTFS: Type: LOGGED_UTILITY_STREAM\n")); break;
458 default:
459 if (RT_LE2H_U32(pHdr->uAttrType) >= RT_LE2H_U32_C(NTFS_AT_FIRST_USER_DEFINED))
460 Log2(("NTFS: Type: unknown user defined - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
461 else
462 Log2(("NTFS: Type: unknown - %#x!\n", RT_LE2H_U32(pHdr->uAttrType)));
463 break;
464 }
465
466 size_t const cbMaxAttrib = cbRec - offRec;
467 if (!pHdr->fNonResident)
468 {
469 uint16_t const offValue = RT_LE2H_U16(pHdr->u.Res.offValue);
470 uint32_t const cbValue = RT_LE2H_U32(pHdr->u.Res.cbValue);
471 Log2(("NTFS: Value: %#x LB %#x, fFlags=%#x bReserved=%#x\n",
472 offValue, cbValue, pHdr->u.Res.fFlags, pHdr->u.Res.bReserved));
473 if ( offValue < cbMaxAttrib
474 && cbValue < cbMaxAttrib
475 && offValue + cbValue <= cbMaxAttrib)
476 {
477 uint8_t const *pbValue = &pbRec[offRec + offValue];
478 RTTIMESPEC Spec;
479 char sz[80];
480 switch (pHdr->uAttrType)
481 {
482 case NTFS_AT_STANDARD_INFORMATION:
483 {
484 PCNTFSATSTDINFO pInfo = (PCNTFSATSTDINFO)pbValue;
485 if (cbValue >= NTFSATSTDINFO_SIZE_NTFS_V12)
486 {
487 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
488 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
489 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
490 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
491 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
492 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
493 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
494 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
495 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
496 Log2(("NTFS: cMaxFileVersions %#RX32\n", RT_LE2H_U32(pInfo->cMaxFileVersions) ));
497 Log2(("NTFS: uFileVersion %#RX32\n", RT_LE2H_U32(pInfo->uFileVersion) ));
498 }
499 else
500 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATSTDINFO!\n",
501 cbValue, NTFSATSTDINFO_SIZE_NTFS_V12));
502 if (cbValue >= sizeof(*pInfo))
503 {
504 Log2(("NTFS: idClass %#RX32\n", RT_LE2H_U32(pInfo->idClass) ));
505 Log2(("NTFS: idOwner %#RX32\n", RT_LE2H_U32(pInfo->idOwner) ));
506 Log2(("NTFS: idSecurity %#RX32\n", RT_LE2H_U32(pInfo->idSecurity) ));
507 Log2(("NTFS: cbQuotaChared %#RX64\n", RT_LE2H_U64(pInfo->cbQuotaChared) ));
508 Log2(("NTFS: idxUpdateSequence %#RX64\n", RT_LE2H_U64(pInfo->idxUpdateSequence) ));
509 }
510 if (cbValue > sizeof(*pInfo))
511 Log2(("NTFS: Undefined data: %.*Rhxs\n", cbValue - sizeof(*pInfo), &pbValue[sizeof(*pInfo)]));
512 break;
513 }
514
515 case NTFS_AT_ATTRIBUTE_LIST:
516 {
517 uint32_t iEntry = 0;
518 uint32_t offEntry = 0;
519 while (offEntry + NTFSATLISTENTRY_SIZE_MINIMAL < cbValue)
520 {
521 PCNTFSATLISTENTRY pInfo = (PCNTFSATLISTENTRY)&pbValue[offEntry];
522 Log2(("NTFS: attr[%u]: %#x in %#RX64 (sqn %#x), instance %#x, VNC=%#RX64-, name %#x L %#x\n",
523 iEntry, RT_LE2H_U32(pInfo->uAttrType), NTFSMFTREF_GET_IDX(&pInfo->InMftRec),
524 NTFSMFTREF_GET_SEQ(&pInfo->InMftRec), RT_LE2H_U16(pInfo->idAttrib),
525 RT_LE2H_U64(pInfo->iVcnFirst), pInfo->offName, pInfo->cwcName));
526 if ( pInfo->cwcName > 0
527 && pInfo->offName < pInfo->cbEntry)
528 Log2(("NTFS: name '%.*ls'\n", pInfo->cwcName, (uint8_t *)pInfo + pInfo->offName));
529
530 /* next */
531 if (pInfo->cbEntry < NTFSATLISTENTRY_SIZE_MINIMAL)
532 {
533 Log2(("NTFS: cbEntry is too small! cbEntry=%#x, min %#x\n",
534 pInfo->cbEntry, NTFSATLISTENTRY_SIZE_MINIMAL));
535 break;
536 }
537 iEntry++;
538 offEntry += RT_ALIGN_32(pInfo->cbEntry, 8);
539 }
540 break;
541 }
542
543 case NTFS_AT_FILENAME:
544 {
545 PCNTFSATFILENAME pInfo = (PCNTFSATFILENAME)pbValue;
546 if (cbValue >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
547 {
548 Log2(("NTFS: ParentDirMftRec %#RX64, sqn %#x\n",
549 NTFSMFTREF_GET_IDX(&pInfo->ParentDirMftRec), NTFSMFTREF_GET_SEQ(&pInfo->ParentDirMftRec) ));
550 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iCreationTime),
551 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iCreationTime)), sz, sizeof(sz)) ));
552 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastDataModTime),
553 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastDataModTime)), sz, sizeof(sz)) ));
554 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastMftModTime),
555 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastMftModTime)), sz, sizeof(sz)) ));
556 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pInfo->iLastAccessTime),
557 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pInfo->iLastAccessTime)), sz, sizeof(sz)) ));
558 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
559 RT_LE2H_U64(pInfo->cbAllocated), RT_LE2H_U64(pInfo->cbAllocated)));
560 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
561 RT_LE2H_U64(pInfo->cbData), RT_LE2H_U64(pInfo->cbData)));
562 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pInfo->fFileAttribs) ));
563 if (RT_LE2H_U32(pInfo->fFileAttribs) & NTFS_FA_REPARSE_POINT)
564 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pInfo->u.uReparseTag) ));
565 else
566 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pInfo->u.cbPackedEas) ));
567 Log2(("NTFS: cwcFilename %#x\n", pInfo->cwcFilename));
568 Log2(("NTFS: fFilenameType %#x\n", pInfo->fFilenameType));
569 if (cbValue >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
570 Log2(("NTFS: wszFilename '%.*ls'\n", pInfo->cwcFilename, pInfo->wszFilename ));
571 else
572 Log2(("NTFS: Error! Truncated filename!!\n"));
573 }
574 else
575 Log2(("NTFS: Error! cbValue=%#x is smaller than expected (%#x) for NTFSATFILENAME!\n",
576 cbValue, RT_OFFSETOF(NTFSATFILENAME, wszFilename) ));
577 break;
578 }
579
580 //case NTFS_AT_OBJECT_ID:
581 //case NTFS_AT_SECURITY_DESCRIPTOR:
582 //case NTFS_AT_VOLUME_NAME:
583 //case NTFS_AT_VOLUME_INFORMATION:
584 //case NTFS_AT_DATA:
585
586 case NTFS_AT_INDEX_ROOT:
587 rtFsNtfsVol_LogIndexRoot((PCNTFSATINDEXROOT)pbValue, cbValue);
588 break;
589
590 //case NTFS_AT_INDEX_ALLOCATION:
591 //case NTFS_AT_BITMAP:
592 //case NTFS_AT_REPARSE_POINT:
593 //case NTFS_AT_EA_INFORMATION:
594 //case NTFS_AT_EA:
595 //case NTFS_AT_PROPERTY_SET:
596 //case NTFS_AT_LOGGED_UTILITY_STREAM:
597
598 default:
599 if (cbValue <= 24)
600 Log2(("NTFS: %.*Rhxs\n", cbValue, pbValue));
601 else
602 Log2(("%.*Rhxd\n", cbValue, pbValue));
603 break;
604 }
605
606 }
607 else
608 Log2(("NTFS: !Value is out of bounds!\n"));
609 }
610 else if (RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED) <= cbMaxAttrib)
611 {
612 Log2(("NTFS: VNC range %#RX64 .. %#RX64 (%#RX64 clusters)\n",
613 RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst), RT_LE2H_U64(pHdr->u.NonRes.iVcnLast),
614 RT_LE2H_U64(pHdr->u.NonRes.iVcnLast) - RT_LE2H_U64(pHdr->u.NonRes.iVcnFirst) + 1));
615 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
616 RT_LE2H_U64(pHdr->u.NonRes.cbAllocated), RT_LE2H_U64(pHdr->u.NonRes.cbAllocated)));
617 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
618 RT_LE2H_U64(pHdr->u.NonRes.cbData), RT_LE2H_U64(pHdr->u.NonRes.cbData)));
619 Log2(("NTFS: cbInitialized %#RX64 (%Rhcb)\n",
620 RT_LE2H_U64(pHdr->u.NonRes.cbInitialized), RT_LE2H_U64(pHdr->u.NonRes.cbInitialized)));
621 uint16_t const offMappingPairs = RT_LE2H_U16(pHdr->u.NonRes.offMappingPairs);
622 Log2(("NTFS: offMappingPairs %#RX16\n", offMappingPairs));
623 if ( pHdr->u.NonRes.abReserved[0] || pHdr->u.NonRes.abReserved[1]
624 || pHdr->u.NonRes.abReserved[2] || pHdr->u.NonRes.abReserved[3] || pHdr->u.NonRes.abReserved[4] )
625 Log2(("NTFS: abReserved %.7Rhxs\n", pHdr->u.NonRes.abReserved));
626 if (pHdr->u.NonRes.uCompressionUnit != 0)
627 Log2(("NTFS: Compression unit 2^%u clusters\n", pHdr->u.NonRes.uCompressionUnit));
628
629 if ( cbMaxAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
630 && cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
631 && ( offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
632 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED))
633 Log2(("NTFS: cbCompressed %#RX64 (%Rhcb)\n",
634 RT_LE2H_U64(pHdr->u.NonRes.cbCompressed), RT_LE2H_U64(pHdr->u.NonRes.cbCompressed)));
635 else if ( pHdr->u.NonRes.uCompressionUnit != 0
636 && pHdr->u.NonRes.uCompressionUnit != 64
637 && pHdr->u.NonRes.iVcnFirst == 0)
638 Log2(("NTFS: !Error! Compressed attrib fields are out of bound!\n"));
639
640 if ( offMappingPairs < cbAttrib
641 && offMappingPairs >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
642 {
643 uint8_t const *pbPairs = &pbRec[offRec + offMappingPairs];
644 uint32_t const cbMaxPairs = cbAttrib - offMappingPairs;
645 int64_t iVnc = pHdr->u.NonRes.iVcnFirst;
646 if (cbMaxPairs < 48)
647 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x %.*Rhxs\n", cbMaxPairs, cbMaxPairs, pbPairs));
648 else
649 Log2(("NTFS: Mapping Pairs: cbMaxPairs=%#x\n%.*Rhxd\n", cbMaxPairs, cbMaxPairs, pbPairs));
650 if (!iVnc && !*pbPairs)
651 Log2(("NTFS: [0]: Empty\n"));
652 else
653 {
654 if (iVnc != 0)
655 Log2(("NTFS: [00/0x000]: VCN=%#012RX64 L %#012RX64 - not mapped\n", 0, iVnc));
656 int64_t iLnc = 0;
657 uint32_t iPair = 0;
658 uint32_t offPairs = 0;
659 while (offPairs < cbMaxPairs)
660 {
661 /* First byte: 4-bit length of each of the pair values */
662 uint8_t const bLengths = pbPairs[offPairs];
663 if (!bLengths)
664 break;
665 uint8_t const cbRun = (bLengths & 0x0f) + (bLengths >> 4);
666 if (offPairs + cbRun > cbMaxPairs)
667 {
668 Log2(("NTFS: [%02d/%#05x]: run overrun! cbRun=%#x bLengths=%#x offPairs=%#x cbMaxPairs=%#x\n",
669 iPair, offPairs, cbRun, bLengths, offPairs, cbMaxPairs));
670 break;
671 }
672 //Log2(("NTFS: @%#05x: %.*Rhxs\n", offPairs, cbRun + 1, &pbPairs[offPairs]));
673
674 /* Value 1: Number of (virtual) clusters in this run. */
675 int64_t cClustersInRun;
676 uint8_t cbNum = (bLengths & 0xf);
677 if (cbNum)
678 {
679 uint8_t const *pbNum = &pbPairs[offPairs + cbNum]; /* last byte */
680 cClustersInRun = (int8_t)*pbNum--;
681 while (cbNum-- > 1)
682 cClustersInRun = (cClustersInRun << 8) + *pbNum--;
683 }
684 else
685 cClustersInRun = -1;
686
687 /* Value 2: The logical cluster delta to get to the first cluster in the run. */
688 cbNum = bLengths >> 4;
689 if (cbNum)
690 {
691 uint8_t const *pbNum = &pbPairs[offPairs + cbNum + (bLengths & 0xf)]; /* last byte */
692 int64_t cLcnDelta = (int8_t)*pbNum--;
693 while (cbNum-- > 1)
694 cLcnDelta = (cLcnDelta << 8) + *pbNum--;
695 iLnc += cLcnDelta;
696 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => LNC=%#012RX64\n",
697 iPair, offPairs, iVnc, cClustersInRun, iLnc));
698 }
699 else
700 Log2(("NTFS: [%02d/%#05x]: VNC=%#012RX64 L %#012RX64 => HOLE\n",
701 iPair, offPairs, iVnc, cClustersInRun));
702
703 /* Advance. */
704 iVnc += cClustersInRun;
705 offPairs += 1 + cbRun;
706 iPair++;
707 }
708 }
709 }
710 else if ( cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED
711 && cbAttrib != NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
712 {
713 Log2(("NTFS: Warning! Odd non-resident attribute size: %#x!\n", cbAttrib));
714 if (cbAttrib >= NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
715 Log2(("NTFS: @%05x: %.*Rhxs!\n", offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
716 cbAttrib - NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED,
717 &pbRec[offRec + NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED]));
718 }
719 }
720 else
721 Log2(("NTFS: !Attrib header is out of bound!\n"));
722
723 /* Advance. */
724 offRec += RT_MAX(cbAttrib, NTFSATTRIBHDR_SIZE_RESIDENT);
725 }
726
727 /* Anything left? */
728 if (offRec < cbRecUsed)
729 Log2(("NTFS: @%#05x: Tail: %.*Rhxs\n", offRec, cbRecUsed - offRec, &pbRec[offRec]));
730 }
731 else
732 Log2(("NTFS: Unknown record type: %.4Rhxs\n", pFileRec));
733 }
734}
735#endif /* LOG_ENABLED */
736
737
738static int rtFsNtfsAttr_ParseExtents(PRTFSNTFSATTR pAttrib, PRTFSNTFSEXTENTS pExtents, uint8_t cClusterShift, int64_t iVcnFirst,
739 uint64_t cbVolume, PRTERRINFO pErrInfo, uint64_t idxMft, uint32_t offAttrib)
740{
741 PCNTFSATTRIBHDR pAttrHdr = pAttrib->pAttrHdr;
742 Assert(pAttrHdr->fNonResident);
743 Assert(pExtents->cExtents == 0);
744 Assert(pExtents->paExtents == NULL);
745
746 /** @todo Not entirely sure how to best detect empty mapping pair program.
747 * Not sure if this is a real problem as zero length stuff can be
748 * resident. */
749 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
750 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
751 if ( offMappingPairs != cbAttrib
752 && offMappingPairs != 0)
753 {
754 if (pAttrHdr->u.NonRes.iVcnFirst < iVcnFirst)
755 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
756 "Bad MFT record %#RX64: Attribute (@%#x) has a lower starting VNC than expected: %#RX64, %#RX64",
757 idxMft, offAttrib, pAttrHdr->u.NonRes.iVcnFirst, iVcnFirst);
758
759 if ( offMappingPairs >= cbAttrib
760 || offMappingPairs < NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED)
761 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
762 "Bad MFT record %#RX64: Mapping pair program for attribute (@%#x) is out of bounds: %#x, cbAttrib=%#x",
763 idxMft, offAttrib, offMappingPairs, cbAttrib);
764
765 /*
766 * Count the pairs.
767 */
768 uint8_t const * const pbPairs = (const uint8_t *)pAttrHdr + pAttrHdr->u.NonRes.offMappingPairs;
769 uint32_t const cbPairs = cbAttrib - offMappingPairs;
770 uint32_t offPairs = 0;
771 uint32_t cPairs = 0;
772 while (offPairs < cbPairs)
773 {
774 uint8_t const bLengths = pbPairs[offPairs];
775 if (bLengths)
776 {
777 uint8_t const cbRunField = bLengths & 0x0f;
778 uint8_t const cbLcnField = bLengths >> 4;
779 if ( cbRunField > 0
780 && cbRunField <= 8)
781 {
782 if (cbLcnField <= 8)
783 {
784 cPairs++;
785
786 /* Advance and check for overflow/end. */
787 offPairs += 1 + cbRunField + cbLcnField;
788 if (offPairs <= cbAttrib)
789 continue;
790 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
791 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x) is out of bounds",
792 idxMft, cPairs - 1, offAttrib);
793 }
794 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
795 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbLcnField is out of bound: %u",
796 idxMft, cPairs - 1, offAttrib, cbLcnField);
797
798 }
799 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
800 "Bad MFT record %#RX64: Mapping pair #%#x for attribute (@%#x): cbRunField is out of bound: %u",
801 idxMft, cPairs - 1, offAttrib, cbRunField);
802 }
803 break;
804 }
805
806 /*
807 * Allocate an the extent table for them.
808 */
809 uint32_t const cExtents = cPairs + (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst);
810 if (cExtents)
811 {
812 PRTFSNTFSEXTENT paExtents = (PRTFSNTFSEXTENT)RTMemAllocZ(sizeof(paExtents[0]) * cExtents);
813 AssertReturn(paExtents, VERR_NO_MEMORY);
814
815 /*
816 * Fill the table.
817 */
818 uint32_t iExtent = 0;
819
820 /* A sparse hole between this and the previous extent table? */
821 if (pAttrHdr->u.NonRes.iVcnFirst != iVcnFirst)
822 {
823 paExtents[iExtent].off = UINT64_MAX;
824 paExtents[iExtent].cbExtent = (pAttrHdr->u.NonRes.iVcnFirst - iVcnFirst) << cClusterShift;
825 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
826 iExtent++;
827 }
828
829 /* Run the program again, now with values and without verbose error checking. */
830 uint64_t cMaxClustersInRun = (INT64_MAX >> cClusterShift) - pAttrHdr->u.NonRes.iVcnFirst;
831 uint64_t cbData = 0;
832 int64_t iLcn = 0;
833 int rc = VINF_SUCCESS;
834 offPairs = 0;
835 for (; iExtent < cExtents; iExtent++)
836 {
837 uint8_t const bLengths = pbPairs[offPairs++];
838 uint8_t const cbRunField = bLengths & 0x0f;
839 uint8_t const cbLcnField = bLengths >> 4;
840 AssertBreakStmt((unsigned)cbRunField - 1U <= 7U, rc = VERR_VFS_BOGUS_FORMAT);
841 AssertBreakStmt((unsigned)cbLcnField <= 8U, rc = VERR_VFS_BOGUS_FORMAT);
842
843 AssertBreakStmt(!(pbPairs[offPairs + cbRunField - 1] & 0x80),
844 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
845 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): Negative runlength value",
846 idxMft, iExtent, offAttrib));
847 uint64_t cClustersInRun = 0;
848 switch (cbRunField)
849 {
850 case 8: cClustersInRun |= (uint64_t)pbPairs[offPairs + 7] << 56; RT_FALL_THRU();
851 case 7: cClustersInRun |= (uint64_t)pbPairs[offPairs + 6] << 48; RT_FALL_THRU();
852 case 6: cClustersInRun |= (uint64_t)pbPairs[offPairs + 5] << 40; RT_FALL_THRU();
853 case 5: cClustersInRun |= (uint64_t)pbPairs[offPairs + 4] << 32; RT_FALL_THRU();
854 case 4: cClustersInRun |= (uint32_t)pbPairs[offPairs + 3] << 24; RT_FALL_THRU();
855 case 3: cClustersInRun |= (uint32_t)pbPairs[offPairs + 2] << 16; RT_FALL_THRU();
856 case 2: cClustersInRun |= (uint16_t)pbPairs[offPairs + 1] << 8; RT_FALL_THRU();
857 case 1: cClustersInRun |= (uint16_t)pbPairs[offPairs + 0] << 0; RT_FALL_THRU();
858 }
859 offPairs += cbRunField;
860 AssertBreakStmt(cClustersInRun <= cMaxClustersInRun,
861 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
862 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): too many clusters %#RX64, max %#RX64",
863 idxMft, iExtent, offAttrib, cClustersInRun, cMaxClustersInRun));
864 cMaxClustersInRun -= cClustersInRun;
865 paExtents[iExtent].cbExtent = cClustersInRun << cClusterShift;
866 cbData += cClustersInRun << cClusterShift;
867
868 if (cbLcnField)
869 {
870 unsigned offVncDelta = cbLcnField;
871 int64_t cLncDelta = (int8_t)pbPairs[--offVncDelta + offPairs];
872 while (offVncDelta-- > 0)
873 cLncDelta = (cLncDelta << 8) | pbPairs[offVncDelta + offPairs];
874 offPairs += cbLcnField;
875
876 iLcn += cLncDelta;
877 if (iLcn >= 0)
878 {
879 paExtents[iExtent].off = (uint64_t)iLcn << cClusterShift;
880 AssertBreakStmt((paExtents[iExtent].off >> cClusterShift) == (uint64_t)iLcn,
881 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
882 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x): iLcn %RX64 overflows when shifted by %u",
883 idxMft, iExtent, offAttrib, iLcn, cClusterShift));
884 AssertBreakStmt( paExtents[iExtent].off < cbVolume
885 || paExtents[iExtent].cbExtent < cbVolume
886 || paExtents[iExtent].off + paExtents[iExtent].cbExtent <= cbVolume,
887 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
888 "Bad MFT record %#RX64: Extent #%#x for attribute (@%#x) outside volume: %#RX64 LB %#RX64, cbVolume=%#RX64",
889 idxMft, iExtent, offAttrib, paExtents[iExtent].off,
890 paExtents[iExtent].cbExtent, cbVolume));
891 }
892 else
893 paExtents[iExtent].off = UINT64_MAX;
894 }
895 else
896 paExtents[iExtent].off = UINT64_MAX;
897 Log3((" paExtent[%#04x]: %#018RX64 LB %#010RX64\n", iExtent, paExtents[iExtent].off, paExtents[iExtent].cbExtent));
898 }
899
900 /* Commit if everything went fine? */
901 if (RT_SUCCESS(rc))
902 {
903 pExtents->cbData = cbData;
904 pExtents->cExtents = cExtents;
905 pExtents->paExtents = paExtents;
906 }
907 else
908 {
909 RTMemFree(paExtents);
910 return rc;
911 }
912 }
913 }
914 return VINF_SUCCESS;
915}
916
917
918/**
919 * Parses the given MTF record and all related records, putting the result in
920 * pRec->pCore (with one reference for the caller).
921 *
922 * ASSUMES caller will release pRec->pCore on failure.
923 *
924 * @returns IPRT status code.
925 * @param pThis The volume.
926 * @param pRec The MFT record to parse.
927 * @param pErrInfo Where to return additional error information. Optional.
928 */
929static int rtFsNtfsVol_ParseMft(PRTFSNTFSVOL pThis, PRTFSNTFSMFTREC pRec, PRTERRINFO pErrInfo)
930{
931 AssertReturn(!pRec->pCore, VERR_INTERNAL_ERROR_4);
932
933 /*
934 * Check that it is a file record and that its base MFT record number is zero.
935 * Caller should do the base record resolving.
936 */
937 PNTFSRECFILE pFileRec = pRec->pFileRec;
938 if (pFileRec->Hdr.uMagic != NTFSREC_MAGIC_FILE)
939 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
940 "Bad MFT record %#RX64: Not a FILE entry (%.4Rhxs)",
941 pRec->TreeNode.Key, &pFileRec->Hdr);
942 if (pFileRec->BaseMftRec.u64 != 0)
943 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
944 "Bad MFT record %#RX64: Not a base record (%#RX64, sqn %#x)",
945 pRec->TreeNode.Key, NTFSMFTREF_GET_IDX(&pFileRec->BaseMftRec),
946 NTFSMFTREF_GET_SEQ(&pFileRec->BaseMftRec) );
947
948 /*
949 * Create a core node (1 reference, returned even on error).
950 */
951 PRTFSNTFSCORE pCore = (PRTFSNTFSCORE)RTMemAllocZ(sizeof(*pCore));
952 AssertReturn(pCore, VERR_NO_MEMORY);
953
954 pCore->cRefs = 1;
955 pCore->pVol = pThis;
956 RTListInit(&pCore->AttribHead);
957 pCore->pMftRec = pRec;
958 rtFsNtfsMftRec_Retain(pRec);
959 pRec->pCore = pCore;
960
961 /*
962 * Parse attributes.
963 * We process any attribute list afterwards, skipping attributes in this MFT record.
964 */
965 PRTFSNTFSATTR pAttrList = NULL;
966 uint8_t * const pbRec = pRec->pbRec;
967 uint32_t offRec = pFileRec->offFirstAttrib;
968 uint32_t const cbRecUsed = RT_MIN(pThis->cbMftRecord, pFileRec->cbRecUsed);
969 while (offRec + NTFSATTRIBHDR_SIZE_RESIDENT <= cbRecUsed)
970 {
971 PNTFSATTRIBHDR pAttrHdr = (PNTFSATTRIBHDR)&pbRec[offRec];
972
973 /*
974 * Validate the attribute data.
975 */
976 uint32_t const cbAttrib = RT_LE2H_U32(pAttrHdr->cbAttrib);
977 uint32_t const cbMin = !pAttrHdr->fNonResident ? NTFSATTRIBHDR_SIZE_RESIDENT
978 : pAttrHdr->u.NonRes.uCompressionUnit == 0 ? NTFSATTRIBHDR_SIZE_NONRES_UNCOMPRESSED
979 : NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED;
980 if (cbAttrib < cbMin)
981 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
982 "Bad MFT record %#RX64: Attribute (@%#x) is too small (%#x, cbMin=%#x)",
983 pRec->TreeNode.Key, offRec, cbAttrib, cbMin);
984 if (offRec + cbAttrib > cbRecUsed)
985 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
986 "Bad MFT record %#RX64: Attribute (@%#x) is too long (%#x, cbRecUsed=%#x)",
987 pRec->TreeNode.Key, offRec, cbAttrib, cbRecUsed);
988 if (cbAttrib & 0x7)
989 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
990 "Bad MFT record %#RX64: Attribute (@%#x) size is misaligned: %#x",
991 pRec->TreeNode.Key, offRec, cbAttrib);
992 if (pAttrHdr->fNonResident)
993 {
994 int64_t const iVcnFirst = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnFirst);
995 int64_t const iVcnLast = RT_LE2H_U64(pAttrHdr->u.NonRes.iVcnLast);
996 if (iVcnFirst > iVcnLast)
997 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
998 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is higher than iVcnLast (%#RX64)",
999 pRec->TreeNode.Key, offRec, iVcnFirst, iVcnLast);
1000 if (iVcnFirst < 0)
1001 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1002 "Bad MFT record %#RX64: Attribute (@%#x): iVcnFirst (%#RX64) is negative",
1003 pRec->TreeNode.Key, offRec, iVcnFirst);
1004 if ((uint64_t)iVcnLast > pThis->iMaxVirtualCluster)
1005 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1006 "Bad MFT record %#RX64: Attribute (@%#x): iVcnLast (%#RX64) is too high, max %RX64 (shift %#x)",
1007 pRec->TreeNode.Key, offRec, iVcnLast, pThis->cClusterShift, pThis->iMaxVirtualCluster);
1008 uint16_t const offMappingPairs = RT_LE2H_U16(pAttrHdr->u.NonRes.offMappingPairs);
1009 if ( (offMappingPairs != 0 && offMappingPairs < cbMin)
1010 || offMappingPairs > cbAttrib)
1011 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1012 "Bad MFT record %#RX64: Attribute (@%#x): offMappingPairs (%#x) is out of bounds (cbAttrib=%#x, cbMin=%#x)",
1013 pRec->TreeNode.Key, offRec, offMappingPairs, cbAttrib, cbMin);
1014 if (pAttrHdr->u.NonRes.uCompressionUnit > 16)
1015 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1016 "Bad MFT record %#RX64: Attribute (@%#x): uCompressionUnit (%#x) is too high",
1017 pRec->TreeNode.Key, offRec, pAttrHdr->u.NonRes.uCompressionUnit);
1018
1019 int64_t const cbAllocated = RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated);
1020 if (cbAllocated < 0)
1021 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1022 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) is negative",
1023 pRec->TreeNode.Key, offRec, cbAllocated);
1024 if ((uint64_t)cbAllocated & (pThis->cbCluster - 1))
1025 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1026 "Bad MFT record %#RX64: Attribute (@%#x): cbAllocated (%#RX64) isn't cluster aligned (cbCluster=%#x)",
1027 pRec->TreeNode.Key, offRec, cbAllocated, pThis->cbCluster);
1028
1029 int64_t const cbData = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1030 if (cbData < 0)
1031 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1032 "Bad MFT record %#RX64: Attribute (@%#x): cbData (%#RX64) is negative",
1033 pRec->TreeNode.Key, offRec, cbData);
1034
1035 int64_t const cbInitialized = RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized);
1036 if (cbInitialized < 0)
1037 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1038 "Bad MFT record %#RX64: Attribute (@%#x): cbInitialized (%#RX64) is negative",
1039 pRec->TreeNode.Key, offRec, cbInitialized);
1040 if (cbMin >= NTFSATTRIBHDR_SIZE_NONRES_COMPRESSED)
1041 {
1042 int64_t const cbCompressed = RT_LE2H_U64(pAttrHdr->u.NonRes.cbCompressed);
1043 if (cbAllocated < 0)
1044 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1045 "Bad MFT record %#RX64: Attribute (@%#x): cbCompressed (%#RX64) is negative",
1046 pRec->TreeNode.Key, offRec, cbCompressed);
1047 }
1048 }
1049 else
1050 {
1051 uint16_t const offValue = RT_LE2H_U32(pAttrHdr->u.Res.offValue);
1052 if ( offValue > cbAttrib
1053 || offValue < NTFSATTRIBHDR_SIZE_RESIDENT)
1054 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1055 "Bad MFT record %#RX64: Attribute (@%#x): offValue (%#RX16) is out of bounds (cbAttrib=%#RX32, cbValue=%#RX32)",
1056 pRec->TreeNode.Key, offRec, offValue, cbAttrib, RT_LE2H_U32(pAttrHdr->u.Res.cbValue));
1057 if ((pAttrHdr->fFlags & NTFS_AF_COMPR_FMT_MASK) != NTFS_AF_COMPR_FMT_NONE)
1058 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1059 "Bad MFT record %#RX64: Attribute (@%#x): fFlags (%#RX16) indicate compression of a resident attribute",
1060 pRec->TreeNode.Key, offRec, RT_LE2H_U16(pAttrHdr->fFlags));
1061 }
1062
1063 if (pAttrHdr->cwcName != 0)
1064 {
1065 uint16_t offName = RT_LE2H_U16(pAttrHdr->offName);
1066 if ( offName < cbMin
1067 || offName >= cbAttrib)
1068 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1069 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) is out of bounds (cbAttrib=%#RX32, cbMin=%#RX32)",
1070 pRec->TreeNode.Key, offRec, offName, cbAttrib, cbMin);
1071 if (offName + pAttrHdr->cwcName * sizeof(RTUTF16) > cbAttrib)
1072 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
1073 "Bad MFT record %#RX64: Attribute (@%#x): offName (%#RX16) + cwcName (%#x) is out of bounds (cbAttrib=%#RX32)",
1074 pRec->TreeNode.Key, offRec, offName, pAttrHdr->cwcName, cbAttrib);
1075 }
1076
1077 /*
1078 * Allocate and initialize a new attribute.
1079 */
1080 PRTFSNTFSATTR pAttrib = (PRTFSNTFSATTR)RTMemAllocZ(sizeof(*pAttrib));
1081 AssertReturn(pAttrib, VERR_NO_MEMORY);
1082 pAttrib->pAttrHdr = pAttrHdr;
1083 pAttrib->offAttrHdrInMftRec = offRec;
1084 pAttrib->pCore = pCore;
1085 //pAttrib->cbResident = 0;
1086 //pAttrib->cbValue = 0;
1087 //pAttrib->Extents.cExtents = 0;
1088 //pAttrib->Extents.paExtents = NULL;
1089 //pAttrib->pSubRecHead = NULL;
1090 if (pAttrHdr->fNonResident)
1091 {
1092 pAttrib->cbValue = RT_LE2H_U64(pAttrHdr->u.NonRes.cbData);
1093 int rc = rtFsNtfsAttr_ParseExtents(pAttrib, &pAttrib->Extents, pThis->cClusterShift, 0 /*iVncFirst*/,
1094 pThis->cbVolume, pErrInfo, pRec->TreeNode.Key, offRec);
1095 if (RT_FAILURE(rc))
1096 {
1097 RTMemFree(pAttrib);
1098 return rc;
1099 }
1100 }
1101 else
1102 {
1103 pAttrib->cbValue = RT_LE2H_U32(pAttrHdr->u.Res.cbValue);
1104 if ( (uint32_t)pAttrib->cbValue > 0
1105 && RT_LE2H_U16(pAttrHdr->u.Res.offValue) < cbAttrib)
1106 {
1107 pAttrib->cbResident = cbAttrib - RT_LE2H_U16(pAttrHdr->u.Res.offValue);
1108 if (pAttrib->cbResident > (uint32_t)pAttrib->cbValue)
1109 pAttrib->cbResident = (uint32_t)pAttrib->cbValue;
1110 }
1111 }
1112
1113 RTListAppend(&pCore->AttribHead, &pAttrib->ListEntry);
1114
1115 if (pAttrHdr->uAttrType == NTFS_AT_ATTRIBUTE_LIST)
1116 pAttrList = pAttrib;
1117
1118 /* Advance. */
1119 offRec += cbAttrib;
1120 }
1121
1122 /*
1123 * Process any attribute list.
1124 */
1125 if (pAttrList)
1126 {
1127 /** @todo */
1128 }
1129
1130 return VINF_SUCCESS;
1131}
1132
1133
1134static int rtFsNtfsAttr_Read(PRTFSNTFSATTR pAttr, uint64_t off, void *pvBuf, size_t cbToRead)
1135{
1136 PRTFSNTFSVOL pVol = pAttr->pCore->pVol;
1137 int rc;
1138 if (!pAttr->pAttrHdr->fNonResident)
1139 {
1140 /*
1141 * The attribute is resident.
1142 */
1143 uint32_t cbAttrib = RT_LE2H_U32(pAttr->pAttrHdr->cbAttrib);
1144 uint32_t cbValue = RT_LE2H_U32(pAttr->pAttrHdr->u.Res.cbValue);
1145 uint16_t offValue = RT_LE2H_U16(pAttr->pAttrHdr->u.Res.offValue);
1146 if ( off < cbValue
1147 && cbToRead <= cbValue
1148 && off + cbToRead <= cbValue)
1149 {
1150 if (offValue <= cbAttrib)
1151 {
1152 cbAttrib -= offValue;
1153 if (off < cbAttrib)
1154 {
1155 /** @todo check if its possible to have cbValue larger than the attribute and
1156 * reading those extra bytes as zero. */
1157 if ( pAttr->offAttrHdrInMftRec + offValue + cbAttrib <= pVol->cbMftRecord
1158 && cbAttrib <= pVol->cbMftRecord)
1159 {
1160 size_t cbToCopy = cbAttrib - off;
1161 if (cbToCopy > cbToRead)
1162 cbToCopy = cbToRead;
1163 memcpy(pvBuf, (uint8_t *)pAttr->pAttrHdr + offValue, cbToCopy);
1164 pvBuf = (uint8_t *)pvBuf + cbToCopy;
1165 cbToRead -= cbToCopy;
1166 rc = VINF_SUCCESS;
1167 }
1168 else
1169 {
1170 rc = VERR_VFS_BOGUS_OFFSET;
1171 Log(("rtFsNtfsAttr_Read: bad resident attribute!\n"));
1172 }
1173 }
1174 else
1175 rc = VINF_SUCCESS;
1176 }
1177 else
1178 rc = VERR_VFS_BOGUS_FORMAT;
1179 }
1180 else
1181 rc = VERR_EOF;
1182 }
1183 else if (pAttr->pAttrHdr->u.NonRes.uCompressionUnit == 0)
1184 {
1185 /*
1186 * Uncompressed non-resident attribute.
1187 */
1188 uint64_t const cbAllocated = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbAllocated);
1189 if ( off >= cbAllocated
1190 || cbToRead > cbAllocated
1191 || off + cbToRead > cbAllocated)
1192 rc = VERR_EOF;
1193 else
1194 {
1195 rc = VINF_SUCCESS;
1196
1197 uint64_t const cbInitialized = RT_LE2H_U64(pAttr->pAttrHdr->u.NonRes.cbInitialized);
1198 if ( off < cbInitialized
1199 && cbToRead > 0)
1200 {
1201 /*
1202 * Locate the first extent. This is a tad complicated.
1203 *
1204 * We move off along as we traverse the extent tables, so that it is relative
1205 * to the start of the current extent.
1206 */
1207 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1208 uint32_t iExtent = 0;
1209 PRTFSNTFSATTRSUBREC pCurSub = NULL;
1210 for (;;)
1211 {
1212 if (off < pTable->cbData)
1213 {
1214 while ( iExtent < pTable->cExtents
1215 && off >= pTable->paExtents[iExtent].cbExtent)
1216 {
1217 off -= pTable->paExtents[iExtent].cbExtent;
1218 iExtent++;
1219 }
1220 AssertReturn(iExtent < pTable->cExtents, VERR_INTERNAL_ERROR_2);
1221 break;
1222 }
1223
1224 /* Next table. */
1225 off -= pTable->cbData;
1226 if (!pCurSub)
1227 pCurSub = pAttr->pSubRecHead;
1228 else
1229 pCurSub = pCurSub->pNext;
1230 if (!pCurSub)
1231 {
1232 iExtent = UINT32_MAX;
1233 break;
1234 }
1235 pTable = &pCurSub->Extents;
1236 iExtent = 0;
1237 }
1238
1239 /*
1240 * The read loop.
1241 */
1242 while (iExtent != UINT32_MAX)
1243 {
1244 uint64_t cbMaxRead = pTable->paExtents[iExtent].cbExtent;
1245 Assert(off < cbMaxRead);
1246 cbMaxRead -= off;
1247 size_t const cbThisRead = cbMaxRead >= cbToRead ? cbToRead : (size_t)cbMaxRead;
1248 if (pTable->paExtents[iExtent].off == UINT64_MAX)
1249 RT_BZERO(pvBuf, cbThisRead);
1250 else
1251 {
1252 rc = RTVfsFileReadAt(pVol->hVfsBacking, pTable->paExtents[iExtent].off + off, pvBuf, cbThisRead, NULL);
1253 Log4(("NTFS: Volume read: @%#RX64 LB %#zx -> %Rrc\n", pTable->paExtents[iExtent].off + off, cbThisRead, rc));
1254 if (RT_FAILURE(rc))
1255 break;
1256 }
1257 pvBuf = (uint8_t *)pvBuf + cbThisRead;
1258 cbToRead -= cbThisRead;
1259 if (!cbToRead)
1260 break;
1261
1262 /*
1263 * Advance to the next extent.
1264 */
1265 iExtent++;
1266 if (iExtent >= pTable->cExtents)
1267 {
1268 pCurSub = pCurSub ? pCurSub->pNext : pAttr->pSubRecHead;
1269 if (!pCurSub)
1270 break;
1271 pTable = &pCurSub->Extents;
1272 iExtent = 0;
1273 }
1274 }
1275 }
1276 }
1277 }
1278 else
1279 {
1280 LogRel(("rtFsNtfsAttr_Read: Compressed files are not supported\n"));
1281 rc = VERR_NOT_SUPPORTED;
1282 }
1283
1284 /*
1285 * Anything else beyond the end of what's stored/initialized?
1286 */
1287 if ( cbToRead > 0
1288 && RT_SUCCESS(rc))
1289 {
1290 RT_BZERO(pvBuf, cbToRead);
1291 }
1292
1293 return rc;
1294}
1295
1296
1297/**
1298 *
1299 * @returns
1300 * @param pRecHdr .
1301 * @param cbRec .
1302 * @param fRelaxedUsa .
1303 * @param pErrInfo .
1304 *
1305 * @see https://msdn.microsoft.com/en-us/library/bb470212%28v=vs.85%29.aspx
1306 */
1307static int rtFsNtfsRec_DoMultiSectorFixups(PNTFSRECHDR pRecHdr, uint32_t cbRec, bool fRelaxedUsa, PRTERRINFO pErrInfo)
1308{
1309 /*
1310 * Do sanity checking.
1311 */
1312 uint16_t offUpdateSeqArray = RT_LE2H_U16(pRecHdr->offUpdateSeqArray);
1313 uint16_t cUpdateSeqEntries = RT_LE2H_U16(pRecHdr->cUpdateSeqEntries);
1314 if ( !(cbRec & (NTFS_MULTI_SECTOR_STRIDE - 1))
1315 && !(offUpdateSeqArray & 1) /* two byte aligned */
1316 && cUpdateSeqEntries == 1 + cbRec / NTFS_MULTI_SECTOR_STRIDE
1317 && offUpdateSeqArray + (uint32_t)cUpdateSeqEntries * 2U < NTFS_MULTI_SECTOR_STRIDE - 2U)
1318 {
1319 uint16_t const *pauUsa = (uint16_t const *)((uint8_t *)pRecHdr + offUpdateSeqArray);
1320
1321 /*
1322 * The first update seqence array entry is the value stored at
1323 * the fixup locations at the end of the blocks. We read this
1324 * and check each of the blocks.
1325 */
1326 uint16_t const uCheck = *pauUsa++;
1327 cUpdateSeqEntries--;
1328 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1329 {
1330 uint16_t const *puBlockCheck = (uint16_t const *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1331 if (*puBlockCheck == uCheck)
1332 { /* likely */ }
1333 else if (!fRelaxedUsa)
1334 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1335 "Multisector transfer error: block #%u ends with %#x instead of %#x (fixup: %#x)",
1336 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) );
1337 else
1338 {
1339 Log(("NTFS: Multisector transfer warning: block #%u ends with %#x instead of %#x (fixup: %#x)\n",
1340 iBlock, RT_LE2H_U16(*puBlockCheck), RT_LE2H_U16(uCheck), RT_LE2H_U16(pauUsa[iBlock]) ));
1341 return VINF_SUCCESS;
1342 }
1343 }
1344
1345 /*
1346 * Apply the fixups.
1347 * Note! We advanced pauUsa above, so it's now at the fixup values.
1348 */
1349 for (uint16_t iBlock = 0; iBlock < cUpdateSeqEntries; iBlock++)
1350 {
1351 uint16_t *puFixup = (uint16_t *)((uint8_t *)pRecHdr + (iBlock + 1) * NTFS_MULTI_SECTOR_STRIDE - 2U);
1352 *puFixup = pauUsa[iBlock];
1353 }
1354 return VINF_SUCCESS;
1355 }
1356 if (fRelaxedUsa)
1357 {
1358 Log(("NTFS: Ignoring bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x\n",
1359 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries ));
1360 return VINF_SUCCESS;
1361 }
1362 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_OFFSET,
1363 "Bogus multisector update sequence: cbRec=%#x uMagic=%#RX32 offUpdateSeqArray=%#x cUpdateSeqEntries=%#x",
1364 cbRec, RT_LE2H_U32(pRecHdr->uMagic), offUpdateSeqArray, cUpdateSeqEntries);
1365}
1366
1367
1368/**
1369 * Allocate and parse an MFT record, returning a core object structure.
1370 *
1371 * @returns IPRT status code.
1372 * @param pThis The NTFS volume instance.
1373 * @param idxMft The index of the MTF record.
1374 * @param fRelaxedUsa Relaxed update sequence checking. Won't fail if
1375 * checks doesn't work or not present.
1376 * @param ppCore Where to return the core object structure.
1377 * @param pErrInfo Where to return error details. Optional.
1378 */
1379static int rtFsNtfsVol_NewCoreForMftIdx(PRTFSNTFSVOL pThis, uint64_t idxMft, bool fRelaxedUsa,
1380 PRTFSNTFSCORE *ppCore, PRTERRINFO pErrInfo)
1381{
1382 *ppCore = NULL;
1383 Assert(pThis->pMftData);
1384 Assert(RTAvlU64Get(&pThis->MftRoot, idxMft) == NULL);
1385
1386 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, idxMft);
1387 AssertReturn(pRec, VERR_NO_MEMORY);
1388
1389 uint64_t offRec = idxMft * pThis->cbMftRecord;
1390 int rc = rtFsNtfsAttr_Read(pThis->pMftData, offRec, pRec->pbRec, pThis->cbMftRecord);
1391 if (RT_SUCCESS(rc))
1392 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, fRelaxedUsa, pErrInfo);
1393 if (RT_SUCCESS(rc))
1394 {
1395#ifdef LOG_ENABLED
1396 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
1397#endif
1398 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
1399 if (RT_SUCCESS(rc))
1400 {
1401 rtFsNtfsMftRec_Release(pRec, pThis);
1402 *ppCore = pRec->pCore;
1403 return VINF_SUCCESS;
1404 }
1405 rtFsNtfsCore_Release(pRec->pCore);
1406 rtFsNtfsMftRec_Release(pRec, pThis);
1407 }
1408 return rc;
1409}
1410
1411
1412/**
1413 * Destroys a core structure.
1414 *
1415 * @returns 0
1416 * @param pThis The core structure.
1417 */
1418static uint32_t rtFsNtfsCore_Destroy(PRTFSNTFSCORE pThis)
1419{
1420 /*
1421 * Free attributes.
1422 */
1423 PRTFSNTFSATTR pCurAttr;
1424 PRTFSNTFSATTR pNextAttr;
1425 RTListForEachSafe(&pThis->AttribHead, pCurAttr, pNextAttr, RTFSNTFSATTR, ListEntry)
1426 {
1427 PRTFSNTFSATTRSUBREC pSub = pCurAttr->pSubRecHead;
1428 while (pSub)
1429 {
1430 pCurAttr->pSubRecHead = pSub->pNext;
1431 RTMemFree(pSub->Extents.paExtents);
1432 pSub->Extents.paExtents = NULL;
1433 pSub->pAttrHdr = NULL;
1434 pSub->pNext = NULL;
1435 RTMemFree(pSub);
1436
1437 pSub = pCurAttr->pSubRecHead;
1438 }
1439
1440 pCurAttr->pCore = NULL;
1441 pCurAttr->pAttrHdr = NULL;
1442 RTMemFree(pCurAttr->Extents.paExtents);
1443 pCurAttr->Extents.paExtents = NULL;
1444 }
1445
1446 /*
1447 * Release the MFT chain.
1448 */
1449 PRTFSNTFSMFTREC pMftRec = pThis->pMftRec;
1450 while (pMftRec)
1451 {
1452 pThis->pMftRec = pMftRec->pNext;
1453 Assert(pMftRec->pCore == pThis);
1454 pMftRec->pNext = NULL;
1455 pMftRec->pCore = NULL;
1456 rtFsNtfsMftRec_Release(pMftRec, pThis->pVol);
1457
1458 pMftRec = pThis->pMftRec;
1459 }
1460
1461 RTMemFree(pThis);
1462
1463 return 0;
1464}
1465
1466
1467/**
1468 * Releases a refernece to a core structure, maybe destroying it.
1469 *
1470 * @returns New reference count.
1471 * @param pThis The core structure.
1472 */
1473static uint32_t rtFsNtfsCore_Release(PRTFSNTFSCORE pThis)
1474{
1475 if (pThis)
1476 {
1477 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1478 Assert(cRefs < 128);
1479 if (cRefs != 0)
1480 return cRefs;
1481 return rtFsNtfsCore_Destroy(pThis);
1482 }
1483 return 0;
1484}
1485
1486
1487#if 0 /* currently unused */
1488/**
1489 * Retains a refernece to a core structure.
1490 *
1491 * @returns New reference count.
1492 * @param pThis The core structure.
1493 */
1494static uint32_t rtFsNtfsCore_Retain(PRTFSNTFSCORE pThis)
1495{
1496 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1497 Assert(cRefs < 128);
1498 return cRefs;
1499}
1500#endif
1501
1502
1503/**
1504 * Finds an unnamed attribute.
1505 *
1506 * @returns Pointer to the attribute structure if found, NULL if not.
1507 * @param pThis The core object structure to search.
1508 * @param uAttrType The attribute type to find.
1509 */
1510static PRTFSNTFSATTR rtFsNtfsCore_FindUnnamedAttribute(PRTFSNTFSCORE pThis, uint32_t uAttrType)
1511{
1512 PRTFSNTFSATTR pCurAttr;
1513 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1514 {
1515 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1516 if ( pAttrHdr->uAttrType == uAttrType
1517 && pAttrHdr->cwcName == 0)
1518 return pCurAttr;
1519 }
1520 return NULL;
1521}
1522
1523
1524/**
1525 * Finds a named attribute, case insensitive ASCII variant.
1526 *
1527 * @returns Pointer to the attribute structure if found, NULL if not.
1528 * @param pThis The core object structure to search.
1529 * @param uAttrType The attribute type to find.
1530 * @param pszAttrib The attribute name, predefined 7-bit ASCII name.
1531 * @param cchAttrib The length of the attribute.
1532 */
1533static PRTFSNTFSATTR rtFsNtfsCore_FindNamedAttributeAscii(PRTFSNTFSCORE pThis, uint32_t uAttrType,
1534 const char *pszAttrib, size_t cchAttrib)
1535{
1536 Assert(cchAttrib > 0);
1537 PRTFSNTFSATTR pCurAttr;
1538 RTListForEach(&pThis->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
1539 {
1540 PNTFSATTRIBHDR pAttrHdr = pCurAttr->pAttrHdr;
1541 if ( pAttrHdr->uAttrType == uAttrType
1542 && pAttrHdr->cwcName == cchAttrib
1543 && RTUtf16NICmpAscii(NTFSATTRIBHDR_GET_NAME(pAttrHdr), pszAttrib, cchAttrib) == 0)
1544 return pCurAttr;
1545 }
1546 return NULL;
1547}
1548
1549
1550
1551/*
1552 *
1553 * NTFS directory code.
1554 * NTFS directory code.
1555 * NTFS directory code.
1556 *
1557 */
1558
1559#ifdef LOG_ENABLED
1560
1561/**
1562 * Logs an index header and all the entries.
1563 *
1564 * @param pIdxHdr The index header.
1565 * @param cbIndex The number of valid bytes starting with the header.
1566 * @param offIndex The offset of the index header into the parent
1567 * structure.
1568 * @param pszPrefix The log prefix.
1569 * @param uIdxType The index type.
1570 */
1571static void rtFsNtfsVol_LogIndexHdrAndEntries(PCNTFSINDEXHDR pIdxHdr, uint32_t cbIndex, uint32_t offIndex,
1572 const char *pszPrefix, uint32_t uIdxType)
1573{
1574 if (!LogIs2Enabled())
1575 return;
1576
1577 /*
1578 * Do the header.
1579 */
1580 if (cbIndex <= sizeof(*pIdxHdr))
1581 {
1582 Log2(("NTFS: %s: Error! Not enough space for the index header! cbIndex=%#x, index head needs %#x\n",
1583 pszPrefix, cbIndex, sizeof(*pIdxHdr)));
1584 return;
1585 }
1586
1587 Log2(("NTFS: %s: offFirstEntry %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->offFirstEntry),
1588 RT_LE2H_U32(pIdxHdr->offFirstEntry) >= cbIndex ? " !out-of-bounds!" : ""));
1589 Log2(("NTFS: %s: cbUsed %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbUsed),
1590 RT_LE2H_U32(pIdxHdr->cbUsed) > cbIndex ? " !out-of-bounds!" : ""));
1591 Log2(("NTFS: %s: cbAllocated %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbAllocated),
1592 RT_LE2H_U32(pIdxHdr->cbAllocated) > cbIndex ? " !out-of-bounds!" : ""));
1593 Log2(("NTFS: %s: fFlags %#x (%s%s)\n", pszPrefix, pIdxHdr->fFlags,
1594 pIdxHdr->fFlags & NTFSINDEXHDR_F_INTERNAL ? "internal" : "leaf",
1595 pIdxHdr->fFlags & ~NTFSINDEXHDR_F_INTERNAL ? " !!unknown-flags!!" : ""));
1596 if (pIdxHdr->abReserved[0]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[0]));
1597 if (pIdxHdr->abReserved[1]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[1]));
1598 if (pIdxHdr->abReserved[2]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[2]));
1599
1600 /*
1601 * The entries.
1602 */
1603 bool fSeenEnd = false;
1604 uint32_t iEntry = 0;
1605 uint32_t offCurEntry = RT_LE2H_U32(pIdxHdr->offFirstEntry);
1606 while (offCurEntry < cbIndex)
1607 {
1608 if (offCurEntry + sizeof(NTFSIDXENTRYHDR) > cbIndex)
1609 {
1610 Log2(("NTFS: Entry[%#04x]: Out of bounds: %#x LB %#x, max %#x\n",
1611 iEntry, offCurEntry, sizeof(NTFSIDXENTRYHDR), cbIndex));
1612 break;
1613 }
1614 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIdxHdr + offCurEntry);
1615 Log2(("NTFS: [%#04x]: @%#05x/@%#05x cbEntry=%#x cbKey=%#x fFlags=%#x (%s%s%s)\n",
1616 iEntry, offCurEntry, offCurEntry + offIndex, RT_LE2H_U16(pEntryHdr->cbEntry), RT_LE2H_U16(pEntryHdr->cbKey),
1617 RT_LE2H_U16(pEntryHdr->fFlags),
1618 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? "internal" : "leaf",
1619 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END ? " end" : "",
1620 pEntryHdr->fFlags & ~(NTFSIDXENTRYHDR_F_INTERNAL | NTFSIDXENTRYHDR_F_END) ? " !unknown!" : ""));
1621 if (uIdxType == NTFSATINDEXROOT_TYPE_DIR)
1622 Log2(("NTFS: FileMftRec %#RX64 sqn %#x\n",
1623 NTFSMFTREF_GET_IDX(&pEntryHdr->u.FileMftRec), NTFSMFTREF_GET_SEQ(&pEntryHdr->u.FileMftRec) ));
1624 else
1625 Log2(("NTFS: offData=%#x cbData=%#x uReserved=%#x\n",
1626 RT_LE2H_U16(pEntryHdr->u.View.offData), RT_LE2H_U16(pEntryHdr->u.View.cbData),
1627 RT_LE2H_U32(pEntryHdr->u.View.uReserved) ));
1628 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
1629 Log2(("NTFS: Subnode=%#RX64\n", RT_LE2H_U64(NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr)) ));
1630
1631 if ( RT_LE2H_U16(pEntryHdr->cbKey) >= sizeof(NTFSATFILENAME)
1632 && uIdxType == NTFSATINDEXROOT_TYPE_DIR)
1633 {
1634 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
1635 Log2(("NTFS: Filename=%.*ls\n", pFilename->cwcFilename, pFilename->wszFilename));
1636 }
1637
1638
1639 /* next */
1640 iEntry++;
1641 offCurEntry += RT_LE2H_U16(pEntryHdr->cbEntry);
1642 fSeenEnd = RT_BOOL(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END);
1643 if (fSeenEnd || RT_LE2H_U16(pEntryHdr->cbEntry) < sizeof(*pEntryHdr))
1644 break;
1645 }
1646 if (!fSeenEnd)
1647 Log2(("NTFS: %s: Warning! Missing NTFSIDXENTRYHDR_F_END node!\n", pszPrefix));
1648}
1649
1650# if 0 /* unused */
1651static void rtFsNtfsVol_LogIndexNode(PCNTFSATINDEXALLOC pIdxNode, uint32_t cbIdxNode, uint32_t uType)
1652{
1653 if (!LogIs2Enabled())
1654 return;
1655 if (cbIdxNode < sizeof(*pIdxNode))
1656 Log2(("NTFS: Index Node: Error! Too small! cbIdxNode=%#x, index node needs %#x\n", cbIdxNode, sizeof(*pIdxNode)));
1657 else
1658 {
1659 Log2(("NTFS: Index Node: uMagic %#x\n", RT_LE2H_U32(pIdxNode->RecHdr.uMagic)));
1660 Log2(("NTFS: Index Node: UpdateSeqArray %#x L %#x\n",
1661 RT_LE2H_U16(pIdxNode->RecHdr.offUpdateSeqArray), RT_LE2H_U16(pIdxNode->RecHdr.cUpdateSeqEntries) ));
1662 Log2(("NTFS: Index Node: uLsn %#RX64\n", RT_LE2H_U64(pIdxNode->uLsn) ));
1663 Log2(("NTFS: Index Node: iSelfAddress %#RX64\n", RT_LE2H_U64(pIdxNode->iSelfAddress) ));
1664 if (pIdxNode->RecHdr.uMagic == NTFSREC_MAGIC_INDEX_ALLOC)
1665 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxNode->Hdr, cbIdxNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
1666 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "Index Node Hdr", uType);
1667 else
1668 Log2(("NTFS: Index Node: !Error! Invalid magic!\n"));
1669 }
1670}
1671# endif
1672
1673/**
1674 * Logs a index root structure and what follows (index header + entries).
1675 *
1676 * @param pIdxRoot The index root.
1677 * @param cbIdxRoot Number of valid bytes starting with @a pIdxRoot.
1678 */
1679static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot)
1680{
1681 if (!LogIs2Enabled())
1682 return;
1683 if (cbIdxRoot < sizeof(*pIdxRoot))
1684 Log2(("NTFS: Index Root: Error! Too small! cbIndex=%#x, index head needs %#x\n", cbIdxRoot, sizeof(*pIdxRoot)));
1685 else
1686 {
1687 Log2(("NTFS: Index Root: cbIdxRoot %#x\n", cbIdxRoot));
1688 Log2(("NTFS: Index Root: uType %#x %s\n", RT_LE2H_U32(pIdxRoot->uType),
1689 pIdxRoot->uType == NTFSATINDEXROOT_TYPE_VIEW ? "view"
1690 : pIdxRoot->uType == NTFSATINDEXROOT_TYPE_DIR ? "directory" : "!unknown!"));
1691 Log2(("NTFS: Index Root: uCollationRules %#x %s\n", RT_LE2H_U32(pIdxRoot->uCollationRules),
1692 pIdxRoot->uCollationRules == NTFS_COLLATION_BINARY ? "binary"
1693 : pIdxRoot->uCollationRules == NTFS_COLLATION_FILENAME ? "filename"
1694 : pIdxRoot->uCollationRules == NTFS_COLLATION_UNICODE_STRING ? "unicode-string"
1695 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32 ? "uint32"
1696 : pIdxRoot->uCollationRules == NTFS_COLLATION_SID ? "sid"
1697 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_PAIR ? "uint32-pair"
1698 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_SEQ ? "uint32-sequence" : "!unknown!"));
1699 Log2(("NTFS: Index Root: cbIndexNode %#x\n", RT_LE2H_U32(pIdxRoot->cbIndexNode) ));
1700 Log2(("NTFS: Index Root: cAddressesPerIndexNode %#x => cbNodeAddressingUnit=%#x\n",
1701 pIdxRoot->cAddressesPerIndexNode, RT_LE2H_U32(pIdxRoot->cbIndexNode) / RT_MAX(1, pIdxRoot->cAddressesPerIndexNode) ));
1702 if (pIdxRoot->abReserved[0]) Log2(("NTFS: Index Root: abReserved[0] %#x\n", pIdxRoot->abReserved[0]));
1703 if (pIdxRoot->abReserved[1]) Log2(("NTFS: Index Root: abReserved[1] %#x\n", pIdxRoot->abReserved[1]));
1704 if (pIdxRoot->abReserved[2]) Log2(("NTFS: Index Root: abReserved[2] %#x\n", pIdxRoot->abReserved[2]));
1705
1706 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxRoot->Hdr, cbIdxRoot - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr),
1707 RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), "Index Root Hdr", pIdxRoot->uType);
1708 }
1709}
1710
1711#endif /* LOG_ENABLED */
1712
1713
1714static int rtFsNtfsVol_NewSharedDirFromCore(PRTFSNTFSVOL pThis, PRTFSNTFSCORE pCore, PRTFSNTFSDIRSHRD *ppSharedDir,
1715 PRTERRINFO pErrInfo, const char *pszWhat)
1716{
1717 *ppSharedDir = NULL;
1718
1719 /*
1720 * Look for the index root and do some quick checks of it first.
1721 */
1722 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
1723 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
1724 if (!pRootAttr)
1725 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: Found no INDEX_ROOT attribute named $I30", pszWhat);
1726 if (pRootAttr->pAttrHdr->fNonResident)
1727 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is is not resident", pszWhat);
1728 if (pRootAttr->cbResident < sizeof(NTFSATINDEXROOT))
1729 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is too small: %#x, min %#x ",
1730 pszWhat, pRootAttr->cbResident, sizeof(pRootAttr->cbResident));
1731
1732 PCNTFSATINDEXROOT pIdxRoot = (PCNTFSATINDEXROOT)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pRootAttr->pAttrHdr);
1733#ifdef LOG_ENABLED
1734 rtFsNtfsVol_LogIndexRoot(pIdxRoot, pRootAttr->cbResident);
1735#endif
1736 NOREF(pIdxRoot);
1737// if (pIdxRoot->uType)
1738// {
1739// }
1740
1741#if 1 /* later */
1742 NOREF(pThis);
1743#else
1744 /*
1745 *
1746 */
1747 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
1748 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
1749 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
1750 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
1751#endif
1752 return VINF_SUCCESS;
1753}
1754
1755
1756/*
1757 *
1758 * Volume level code.
1759 * Volume level code.
1760 * Volume level code.
1761 *
1762 */
1763
1764
1765/**
1766 * Slow path for querying the allocation state of a cluster.
1767 *
1768 * @returns IPRT status code.
1769 * @param pThis The NTFS volume instance.
1770 * @param iCluster The cluster to query.
1771 * @param pfState Where to return the state.
1772 */
1773static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
1774{
1775 int rc;
1776 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData);
1777 uint64_t const offInBitmap = iCluster >> 3;
1778 if (offInBitmap < cbWholeBitmap)
1779 {
1780 if (!pThis->pvBitmap)
1781 {
1782 /*
1783 * Try cache the whole bitmap if it's not too large.
1784 */
1785 if ( cbWholeBitmap <= RTFSNTFS_MAX_BITMAP_CACHE
1786 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8))
1787 {
1788 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8);
1789 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
1790 if (pThis->pvBitmap)
1791 {
1792 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
1793 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap);
1794 if (RT_SUCCESS(rc))
1795 {
1796 pThis->iFirstBitmapCluster = 0;
1797 pThis->cBitmapClusters = pThis->cClusters;
1798 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster);
1799 return VINF_SUCCESS;
1800 }
1801 RTMemFree(pThis->pvBitmap);
1802 pThis->pvBitmap = NULL;
1803 pThis->cbBitmapAlloc = 0;
1804 return rc;
1805 }
1806 }
1807
1808 /*
1809 * Do a cluster/4K cache.
1810 */
1811 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K);
1812 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
1813 if (!pThis->pvBitmap)
1814 {
1815 pThis->cbBitmapAlloc = 0;
1816 return VERR_NO_MEMORY;
1817 }
1818 }
1819
1820 /*
1821 * Load a cache line.
1822 */
1823 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc));
1824 uint64_t offLoad = offInBitmap & ~(pThis->cbBitmapAlloc - 1);
1825 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc);
1826
1827 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
1828 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad);
1829 if (RT_SUCCESS(rc))
1830 {
1831 pThis->iFirstBitmapCluster = offLoad << 3;
1832 pThis->cBitmapClusters = cbLoad << 3;
1833 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster));
1834 return VINF_SUCCESS;
1835 }
1836 pThis->cBitmapClusters = 0;
1837 }
1838 else
1839 {
1840 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap));
1841 rc = VERR_OUT_OF_RANGE;
1842 }
1843 return rc;
1844}
1845
1846
1847/**
1848 * Query the allocation state of the given cluster.
1849 *
1850 * @returns IPRT status code.
1851 * @param pThis The NTFS volume instance.
1852 * @param iCluster The cluster to query.
1853 * @param pfState Where to return the state.
1854 */
1855static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
1856{
1857 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster;
1858 if (iClusterInCache < pThis->cBitmapClusters)
1859 {
1860 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache);
1861 return VINF_SUCCESS;
1862 }
1863 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState);
1864}
1865
1866
1867
1868/**
1869 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
1870 */
1871static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
1872{
1873 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
1874
1875 RTVfsFileRelease(pThis->hVfsBacking);
1876 pThis->hVfsBacking = NIL_RTVFSFILE;
1877 pThis->hVfsSelf = NIL_RTVFS;
1878
1879 return VINF_SUCCESS;
1880}
1881
1882
1883/**
1884 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
1885 */
1886static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1887{
1888 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
1889 return VERR_WRONG_TYPE;
1890}
1891
1892
1893/**
1894 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
1895 */
1896static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
1897{
1898 NOREF(pvThis); NOREF(phVfsDir);
1899 return VERR_NOT_IMPLEMENTED;
1900}
1901
1902
1903/**
1904 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
1905 */
1906static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
1907{
1908 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
1909 *pfUsed = true;
1910
1911 /*
1912 * Round to a cluster range.
1913 */
1914 uint64_t iCluster = off >> pThis->cClusterShift;
1915
1916 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster));
1917 cb += off & (pThis->cbCluster - 1);
1918 cb = RT_ALIGN_Z(cb, pThis->cbCluster);
1919 size_t cClusters = cb >> pThis->cClusterShift;
1920
1921 /*
1922 * Check the clusters one-by-one.
1923 * Just to be cautious, we will always check the cluster at off, even when cb is zero.
1924 */
1925 do
1926 {
1927 bool fState = true;
1928 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
1929 if (RT_FAILURE(rc))
1930 return rc;
1931 if (fState)
1932 {
1933 *pfUsed = true;
1934 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
1935 return VINF_SUCCESS;
1936 }
1937
1938 iCluster++;
1939 } while (cClusters-- > 0);
1940
1941 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
1942 *pfUsed = false;
1943 return VINF_SUCCESS;
1944}
1945
1946
1947DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsNtfsVolOps =
1948{
1949 /* .Obj = */
1950 {
1951 /* .uVersion = */ RTVFSOBJOPS_VERSION,
1952 /* .enmType = */ RTVFSOBJTYPE_VFS,
1953 /* .pszName = */ "NtfsVol",
1954 /* .pfnClose = */ rtFsNtfsVol_Close,
1955 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
1956 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
1957 },
1958 /* .uVersion = */ RTVFSOPS_VERSION,
1959 /* .fFeatures = */ 0,
1960 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
1961 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
1962 /* .uEndMarker = */ RTVFSOPS_VERSION
1963};
1964
1965
1966/**
1967 * Checks that the storage for the given attribute is all marked allocated in
1968 * the allocation bitmap of the volume.
1969 *
1970 * @returns IPRT status code.
1971 * @param pThis The NTFS volume instance.
1972 * @param pAttr The attribute to check.
1973 * @param pszDesc Description of the attribute.
1974 * @param pErrInfo Where to return error details.
1975 */
1976static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo)
1977{
1978 PRTFSNTFSATTRSUBREC pSubRec = NULL;
1979 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
1980 uint64_t offFile = 0;
1981 for (;;)
1982 {
1983 uint32_t const cExtents = pTable->cExtents;
1984 PRTFSNTFSEXTENT paExtents = pTable->paExtents;
1985 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++)
1986 {
1987 uint64_t const off = paExtents[iExtent].off;
1988 if (off == UINT64_MAX)
1989 offFile += paExtents[iExtent].cbExtent;
1990 else
1991 {
1992 uint64_t iCluster = off >> pThis->cClusterShift;
1993 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift;
1994 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent);
1995 Assert(cClusters != 0);
1996
1997 while (cClusters-- > 0)
1998 {
1999 bool fState = false;
2000 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
2001 if (RT_FAILURE(rc))
2002 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc,
2003 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)",
2004 iCluster, pszDesc, offFile);
2005 if (!fState)
2006 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2007 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated",
2008 iCluster, offFile, pszDesc);
2009 offFile += pThis->cbCluster;
2010 }
2011 }
2012 }
2013
2014 /* Next table. */
2015 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead;
2016 if (!pSubRec)
2017 return VINF_SUCCESS;
2018 pTable = &pSubRec->Extents;
2019 }
2020}
2021
2022
2023/**
2024 * Loads, validates and setups the '.' (NTFS_MFT_IDX_ROOT) MFT entry.
2025 *
2026 * @returns IPRT status code
2027 * @param pThis The NTFS volume instance. Will set pawcUpcase.
2028 * @param pErrInfo Where to return additional error info.
2029 */
2030static int rtFsNtfsVolLoadRootDir(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
2031{
2032 /*
2033 * Load it and do some checks.
2034 */
2035 PRTFSNTFSCORE pCore;
2036 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_ROOT, false /*fRelaxedUsa*/, &pCore, pErrInfo); // DON'T COMMIT
2037 if (RT_SUCCESS(rc))
2038 {
2039 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
2040 if (!pFilenameAttr)
2041 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: has no FILENAME attribute!");
2042 else if (pFilenameAttr->pAttrHdr->fNonResident)
2043 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: FILENAME attribute is non-resident!");
2044 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[1]))
2045 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2046 "RootDir: FILENAME attribute value size is too small: %#x",
2047 pFilenameAttr->pAttrHdr->u.Res.cbValue);
2048 else
2049 {
2050 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
2051 + pFilenameAttr->pAttrHdr->u.Res.offValue);
2052 if ( pFilename->cwcFilename != 1
2053 || ( RTUtf16NICmpAscii(pFilename->wszFilename, ".", 1) != 0
2054 && RTUtf16NICmpAscii(pFilename->wszFilename, "$", 1) != 0))
2055 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2056 "RootDir: FILENAME is not '.' nor '$: '%.*ls'",
2057 pFilename->cwcFilename, pFilename->wszFilename);
2058 else
2059 {
2060 PRTFSNTFSATTR pIndexRoot = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
2061 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2062 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
2063 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2064 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
2065 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
2066 if (!pIndexRoot)
2067 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ROOT attribute named $I30");
2068 else if (!pIndexAlloc && pIndexBitmap)
2069 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ALLOCATION attribute named $I30");
2070 else if (!pIndexBitmap && pIndexAlloc)
2071 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no BITMAP attribute named $I30");
2072 if (RT_SUCCESS(rc) && pIndexAlloc)
2073 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexAlloc, "RootDir", pErrInfo);
2074 if (RT_SUCCESS(rc) && pIndexBitmap)
2075 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexBitmap, "RootDir/bitmap", pErrInfo);
2076 if (RT_SUCCESS(rc))
2077 {
2078 /*
2079 * Load it as a normal directory.
2080 */
2081 PRTFSNTFSDIRSHRD pSharedDir;
2082 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, &pSharedDir, pErrInfo, "RootDir");
2083 if (RT_SUCCESS(rc))
2084 {
2085 rtFsNtfsCore_Release(pCore);
2086 pThis->pRootDir = pSharedDir;
2087 return VINF_SUCCESS;
2088 }
2089 }
2090 }
2091 }
2092 rtFsNtfsCore_Release(pCore);
2093 }
2094 else
2095 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Root dir: Error reading MFT record");
2096 return rc;
2097}
2098
2099
2100/**
2101 * Loads, validates and setups the '$UpCase' (NTFS_MFT_IDX_UP_CASE) MFT entry.
2102 *
2103 * This is needed for filename lookups, I think.
2104 *
2105 * @returns IPRT status code
2106 * @param pThis The NTFS volume instance. Will set pawcUpcase.
2107 * @param pErrInfo Where to return additional error info.
2108 */
2109static int rtFsNtfsVolLoadUpCase(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
2110{
2111 PRTFSNTFSCORE pCore;
2112 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_UP_CASE, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2113 if (RT_SUCCESS(rc))
2114 {
2115 PRTFSNTFSATTR pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
2116 if (pDataAttr)
2117 {
2118 /*
2119 * Validate the '$Upcase' MFT record.
2120 */
2121 uint32_t const cbMin = 512;
2122 uint32_t const cbMax = _128K;
2123 if (!pDataAttr->pAttrHdr->fNonResident)
2124 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: unnamed DATA attribute is resident!");
2125 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) < cbMin
2126 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) > cbMax)
2127 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2128 "$UpCase: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX32",
2129 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated), cbMin, cbMax);
2130 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) < cbMin
2131 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
2132 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
2133 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) & 1) )
2134 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2135 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
2136 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData), cbMin,
2137 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
2138 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) < cbMin
2139 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized)
2140 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated)
2141 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) & 1) )
2142 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2143 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
2144 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized), cbMin,
2145 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
2146 else if (pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit != 0)
2147 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2148 "$UpCase: unnamed DATA attribute is compressed: %#x",
2149 pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit);
2150 else
2151 {
2152 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
2153 if (!pFilenameAttr)
2154 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase has no FILENAME attribute!");
2155 else if (pFilenameAttr->pAttrHdr->fNonResident)
2156 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase FILENAME attribute is non-resident!");
2157 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
2158 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2159 "$UpCase: FILENAME attribute value size is too small: %#x",
2160 pFilenameAttr->pAttrHdr->u.Res.cbValue);
2161 else
2162 {
2163 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
2164 + pFilenameAttr->pAttrHdr->u.Res.offValue);
2165 if ( pFilename->cwcFilename != 7
2166 || RTUtf16NICmpAscii(pFilename->wszFilename, "$UpCase", 7) != 0)
2167 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2168 "$UpCase: FILENAME isn't '$UpCase': '%.*ls'",
2169 pFilename->cwcFilename, pFilename->wszFilename);
2170 else
2171 {
2172 /*
2173 * Allocate memory for the uppercase table and read it.
2174 */
2175 PRTUTF16 pawcUpcase;
2176 pThis->pawcUpcase = pawcUpcase = (PRTUTF16)RTMemAlloc(_64K * sizeof(pThis->pawcUpcase[0]));
2177 if (pawcUpcase)
2178 {
2179 for (size_t i = 0; i < _64K; i++)
2180 pawcUpcase[i] = (uint16_t)i;
2181
2182 rc = rtFsNtfsAttr_Read(pDataAttr, 0, pawcUpcase, pDataAttr->pAttrHdr->u.NonRes.cbData);
2183 if (RT_SUCCESS(rc))
2184 {
2185 /*
2186 * Check the data.
2187 */
2188 for (size_t i = 1; i < _64K; i++)
2189 if (pawcUpcase[i] == 0)
2190 {
2191 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2192 "$UpCase entry %#x is zero!", i);
2193 break;
2194 }
2195
2196 /*
2197 * While we still have the $UpCase file open, check it against the allocation bitmap.
2198 */
2199 if (RT_SUCCESS(rc))
2200 rc = rtFsNtfsVolCheckBitmap(pThis, pDataAttr, "$UpCase", pErrInfo);
2201
2202 /* We're done, no need for special success return here though. */
2203 }
2204 else
2205 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, "Error reading $UpCase data into memory");
2206 }
2207 else
2208 rc = VERR_NO_MEMORY;
2209 }
2210 }
2211 }
2212 }
2213 else
2214 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: has no unnamed DATA attribute!");
2215 rtFsNtfsCore_Release(pCore);
2216 }
2217 else
2218 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$UpCase: Error reading the MFT record");
2219 return rc;
2220}
2221
2222
2223/**
2224 * Loads the allocation bitmap and does basic validation of.
2225 *
2226 * @returns IPRT status code.
2227 * @param pThis The NTFS volume instance. Will set up the
2228 * 'Allocation bitmap and cache' fields.
2229 * @param pErrInfo Where to return error details.
2230 */
2231static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
2232{
2233 PRTFSNTFSCORE pCore;
2234 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2235 if (RT_SUCCESS(rc))
2236 {
2237 PRTFSNTFSATTR pMftBitmap;
2238 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
2239 if (pMftBitmap)
2240 {
2241 /*
2242 * Validate the '$Bitmap' MFT record.
2243 * We expect the bitmap to be fully initialized and be sized according to the
2244 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size.
2245 */
2246 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8);
2247 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster);
2248 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8);
2249 if (!pMftBitmap->pAttrHdr->fNonResident)
2250 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!");
2251 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap
2252 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap)
2253 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2254 "$Bitmap: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
2255 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap);
2256 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap
2257 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)
2258 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData))
2259 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2260 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
2261 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap,
2262 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
2263 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap
2264 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized)
2265 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated))
2266 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2267 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
2268 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap,
2269 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
2270 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0)
2271 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2272 "$Bitmap: unnamed DATA attribute is compressed: %#x",
2273 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit);
2274 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */
2275 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2276 "$Bitmap: unnamed DATA attribute is expected to have a single extent: %u extents",
2277 pMftBitmap->Extents.cExtents);
2278 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX)
2279 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse");
2280 else
2281 {
2282 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
2283 if (!pFilenameAttr)
2284 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!");
2285 else if (pFilenameAttr->pAttrHdr->fNonResident)
2286 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!");
2287 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
2288 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2289 "$Bitmap FILENAME attribute value size is too small: %#x",
2290 pFilenameAttr->pAttrHdr->u.Res.cbValue);
2291 else
2292 {
2293 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
2294 + pFilenameAttr->pAttrHdr->u.Res.offValue);
2295 if ( pFilename->cwcFilename != 7
2296 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0)
2297 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2298 "$Bitmap: FILENAME isn't '$Bitmap': '%.*ls'",
2299 pFilename->cwcFilename, pFilename->wszFilename);
2300 else
2301 {
2302 /*
2303 * Read some of it into the buffer and check that essential stuff is flagged as allocated.
2304 */
2305 /* The boot sector. */
2306 bool fState = false;
2307 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState);
2308 if (RT_SUCCESS(rc) && !fState)
2309 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2310 "MFT allocation bitmap error: Bootsector isn't marked allocated!");
2311 else if (RT_FAILURE(rc))
2312 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2313 "MFT allocation bitmap (offset 0) read error: %Rrc", rc);
2314
2315 /* The bitmap ifself, the MFT data, and the MFT bitmap. */
2316 if (RT_SUCCESS(rc))
2317 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo);
2318 if (RT_SUCCESS(rc))
2319 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo);
2320 if (RT_SUCCESS(rc))
2321 rc = rtFsNtfsVolCheckBitmap(pThis,
2322 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP),
2323 "MFT Bitmap", pErrInfo);
2324 if (RT_SUCCESS(rc))
2325 {
2326 /*
2327 * Looks like the bitmap is good.
2328 */
2329 return VINF_SUCCESS;
2330 }
2331 }
2332 }
2333 }
2334 pThis->pMftBitmap = NULL;
2335 }
2336 else
2337 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Bitmap: has no unnamed DATA attribute!");
2338 rtFsNtfsCore_Release(pCore);
2339 }
2340 else
2341 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$Bitmap: Error MFT record");
2342 return rc;
2343}
2344
2345
2346/**
2347 * Loads, validates and setups the '$Volume' (NTFS_MFT_IDX_VOLUME) MFT entry.
2348 *
2349 * @returns IPRT status code
2350 * @param pThis The NTFS volume instance. Will set uNtfsVersion
2351 * and fVolumeFlags.
2352 * @param pErrInfo Where to return additional error info.
2353 */
2354static int rtFsNtfsVolLoadVolumeInfo(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
2355{
2356 PRTFSNTFSCORE pCore;
2357 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_VOLUME, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2358 if (RT_SUCCESS(rc))
2359 {
2360 PRTFSNTFSATTR pVolInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_VOLUME_INFORMATION);
2361 if (pVolInfoAttr)
2362 {
2363 /*
2364 * Validate the '$Volume' MFT record.
2365 */
2366 if (pVolInfoAttr->pAttrHdr->fNonResident)
2367 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume unnamed VOLUME_INFORMATION attribute is not resident!");
2368 else if ( pVolInfoAttr->cbResident != sizeof(NTFSATVOLUMEINFO)
2369 || pVolInfoAttr->cbValue != sizeof(NTFSATVOLUMEINFO))
2370 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2371 "$Volume VOLUME_INFORMATION attribute has the wrong size: cbValue=%#RX64, cbResident=%#RX32, expected %#x\n",
2372 pVolInfoAttr->cbValue, pVolInfoAttr->cbResident, sizeof(NTFSATVOLUMEINFO));
2373 else
2374 {
2375 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
2376 if (!pFilenameAttr)
2377 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume has no FILENAME attribute!");
2378 else if (pFilenameAttr->pAttrHdr->fNonResident)
2379 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume FILENAME attribute is non-resident!");
2380 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
2381 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2382 "$Volume FILENAME attribute value size is too small: %#x",
2383 pFilenameAttr->pAttrHdr->u.Res.cbValue);
2384 else
2385 {
2386 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
2387 + pFilenameAttr->pAttrHdr->u.Res.offValue);
2388 if ( pFilename->cwcFilename != 7
2389 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Volume", 7) != 0)
2390 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2391 "$Volume FILENAME isn't '$Volume': '%.*ls'",
2392 pFilename->cwcFilename, pFilename->wszFilename);
2393 else
2394 {
2395 /*
2396 * Look at the information.
2397 */
2398 PCNTFSATVOLUMEINFO pVolInfo;
2399 pVolInfo = (PCNTFSATVOLUMEINFO)((uint8_t *)pVolInfoAttr->pAttrHdr + pVolInfoAttr->pAttrHdr->u.Res.offValue);
2400 pThis->uNtfsVersion = RTFSNTFS_MAKE_VERSION(pVolInfo->uMajorVersion, pVolInfo->uMinorVersion);
2401 pThis->fVolumeFlags = RT_LE2H_U16(pVolInfo->fFlags);
2402 Log(("NTFS: Version %u.%u, flags=%#x\n", pVolInfo->uMajorVersion, pVolInfo->uMinorVersion, pThis->fVolumeFlags));
2403
2404 /* We're done, no need for special success return here though. */
2405 }
2406 }
2407 }
2408 }
2409 else
2410 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2411 "MFT record $Volume has no unnamed VOLUME_INFORMATION attribute!");
2412 rtFsNtfsCore_Release(pCore);
2413 }
2414 else
2415 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading $Volume MFT record");
2416 return rc;
2417}
2418
2419
2420/**
2421 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry.
2422 *
2423 * This is the first thing we do after we've checked out the boot sector and
2424 * extracted information from it, since everything else depends on us being able
2425 * to access the MFT data.
2426 *
2427 * @returns IPRT status code
2428 * @param pThis The NTFS volume instance. Will set pMftData.
2429 * @param pErrInfo Where to return additional error info.
2430 */
2431static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
2432{
2433 /*
2434 * Bootstrap the MFT data stream.
2435 */
2436 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT);
2437 AssertReturn(pRec, VERR_NO_MEMORY);
2438
2439#if 0 && defined(LOG_ENABLED)
2440 for (uint32_t i = 0; i < 128; i++)
2441 {
2442 uint64_t const offDisk = (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord;
2443 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
2444 if (RT_SUCCESS(rc))
2445 {
2446 pRec->TreeNode.Key = i;
2447 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
2448 pRec->TreeNode.Key = 0;
2449 }
2450 }
2451#endif
2452
2453 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift;
2454 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
2455 if (RT_SUCCESS(rc))
2456 {
2457 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, true, pErrInfo);
2458 if (RT_SUCCESS(rc))
2459 {
2460#ifdef LOG_ENABLED
2461 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
2462#endif
2463 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
2464 }
2465 if (RT_SUCCESS(rc))
2466 {
2467 pThis->pMftData = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_DATA);
2468 if (pThis->pMftData)
2469 {
2470 /*
2471 * Validate the '$Mft' MFT record.
2472 */
2473 if (!pThis->pMftData->pAttrHdr->fNonResident)
2474 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!");
2475 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U
2476 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking)
2477 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2478 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
2479 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbAllocated));
2480 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U
2481 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking)
2482 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2483 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64",
2484 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbInitialized));
2485 else if ( (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U
2486 || (uint64_t)RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking)
2487 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2488 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
2489 RT_LE2H_U64(pThis->pMftData->pAttrHdr->u.NonRes.cbData));
2490 else if (pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit != 0)
2491 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2492 "MFT record #0 unnamed DATA attribute is compressed: %#x",
2493 pThis->pMftData->pAttrHdr->u.NonRes.uCompressionUnit);
2494 else if (pThis->pMftData->Extents.cExtents == 0)
2495 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2496 "MFT record #0 unnamed DATA attribute has no data on the disk");
2497 else if (pThis->pMftData->Extents.paExtents[0].off != offDisk)
2498 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2499 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64",
2500 pThis->pMftData->Extents.paExtents[0].off, offDisk);
2501 else if (!rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_BITMAP))
2502 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!");
2503 else
2504 {
2505 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pRec->pCore, NTFS_AT_FILENAME);
2506 if (!pFilenameAttr)
2507 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!");
2508 else if (pFilenameAttr->pAttrHdr->fNonResident)
2509 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!");
2510 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4]))
2511 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2512 "MFT record #0 FILENAME attribute value size is too small: %#x",
2513 pFilenameAttr->pAttrHdr->u.Res.cbValue);
2514 else
2515 {
2516 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
2517 + pFilenameAttr->pAttrHdr->u.Res.offValue);
2518 if ( pFilename->cwcFilename != 4
2519 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0)
2520 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2521 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'",
2522 pFilename->cwcFilename, pFilename->wszFilename);
2523 else
2524 {
2525 /*
2526 * Looks like we're good.
2527 */
2528 return VINF_SUCCESS;
2529 }
2530 }
2531 }
2532 pThis->pMftData = NULL;
2533 }
2534 else
2535 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!");
2536 }
2537 rtFsNtfsCore_Release(pRec->pCore);
2538 rtFsNtfsMftRec_Release(pRec, pThis);
2539 }
2540 else
2541 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0");
2542 return rc;
2543}
2544
2545
2546/**
2547 * Loads the bootsector and parses it, copying values into the instance data.
2548 *
2549 * @returns IRPT status code.
2550 * @param pThis The instance data.
2551 * @param pvBuf The buffer.
2552 * @param cbBuf The buffer size.
2553 * @param pErrInfo Where to return additional error details.
2554 */
2555static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
2556{
2557 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
2558
2559 /*
2560 * Read the boot sector and check that it makes sense for a NTFS volume.
2561 *
2562 * Note! There are two potential backup locations of the boot sector, however we
2563 * currently don't implement falling back on these on corruption/read errors.
2564 */
2565 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
2566 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
2567 if (RT_FAILURE(rc))
2568 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
2569
2570 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
2571 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
2572 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
2573
2574 /* Check must-be-zero BPB fields. */
2575 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
2576 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
2577 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
2578 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
2579 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
2580 pBootSector->Bpb.Ntfs.Bpb.cFats);
2581 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
2582 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
2583 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
2584 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
2585 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
2586 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
2587 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
2588 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
2589 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
2590
2591 /* Check other relevant BPB fields. */
2592 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
2593 if ( cbSector != 512
2594 && cbSector != 1024
2595 && cbSector != 2048
2596 && cbSector != 4096)
2597 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
2598 pThis->cbSector = cbSector;
2599 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
2600
2601 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
2602 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
2603 || cClusterPerSector == 0)
2604 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
2605 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
2606
2607 pThis->cbCluster = cClusterPerSector * cbSector;
2608 if (pThis->cbCluster > _64K)
2609 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2610 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
2611 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
2612 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
2613 pThis->iMaxVirtualCluster = (uint64_t)INT64_MAX >> pThis->cClusterShift;
2614 Log2(("NTFS BPB: iMaxVirtualCluster=%#RX64\n", pThis->iMaxVirtualCluster));
2615
2616 /* NTFS BPB: cSectors. */
2617 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
2618 if (cSectors > pThis->cbBacking / pThis->cbSector)
2619 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2620 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
2621 cSectors, pThis->cbBacking / pThis->cbSector);
2622 if (cSectors < 256)
2623 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
2624 pThis->cbVolume = cSectors * pThis->cbSector;
2625 pThis->cClusters = cSectors / cClusterPerSector;
2626 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
2627 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
2628
2629 /* NTFS BPB: MFT location. */
2630 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
2631 if ( uLcn < 1
2632 || uLcn >= pThis->cClusters)
2633 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2634 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
2635 pThis->uLcnMft = uLcn;
2636 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
2637
2638 /* NTFS BPB: Mirror MFT location. */
2639 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
2640 if ( uLcn < 1
2641 || uLcn >= pThis->cClusters)
2642 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2643 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
2644 pThis->uLcnMftMirror = uLcn;
2645 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
2646
2647 /* NTFS BPB: Size of MFT file record. */
2648 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
2649 {
2650 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
2651 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
2652 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2653 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
2654 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
2655 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
2656 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
2657 }
2658 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
2659 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
2660 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2661 "NTFS clusters-per-mft-record is out of shift range: %d",
2662 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
2663 else
2664 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
2665 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
2666 if ( pThis->cbMftRecord > _32K
2667 || pThis->cbMftRecord < 256)
2668 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2669 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
2670
2671 /* NTFS BPB: Default index node size */
2672 if (pBootSector->Bpb.Ntfs.cClustersPerIndexNode >= 0)
2673 {
2674 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode)
2675 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode == 0)
2676 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2677 "NTFS default clusters-per-index-tree-node is zero or not a power of two: %#x",
2678 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
2679 pThis->cbDefaultIndexNode = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode << pThis->cClusterShift;
2680 Assert(pThis->cbDefaultIndexNode == pBootSector->Bpb.Ntfs.cClustersPerIndexNode * pThis->cbCluster);
2681 }
2682 else if ( pBootSector->Bpb.Ntfs.cClustersPerIndexNode < -32
2683 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode > -9)
2684 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
2685 "NTFS default clusters-per-index-tree-node is out of shift range: %d",
2686 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
2687 else
2688 pThis->cbDefaultIndexNode = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
2689 Log2(("NTFS BPB: cbDefaultIndexNode=%#x\n", pThis->cbDefaultIndexNode));
2690
2691 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
2692 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
2693
2694
2695 return VINF_SUCCESS;
2696}
2697
2698
2699RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2700{
2701 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2702 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2703 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
2704
2705 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2706 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2707
2708 /*
2709 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
2710 */
2711 RTVFS hVfs;
2712 PRTFSNTFSVOL pThis;
2713 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2714 if (RT_SUCCESS(rc))
2715 {
2716 pThis->hVfsBacking = hVfsFileIn;
2717 pThis->hVfsSelf = hVfs;
2718 pThis->fMntFlags = fMntFlags;
2719 pThis->fNtfsFlags = fNtfsFlags;
2720
2721 rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
2722 if (RT_SUCCESS(rc))
2723 {
2724 void *pvBuf = RTMemTmpAlloc(_64K);
2725 if (pvBuf)
2726 {
2727 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
2728 if (RT_SUCCESS(rc))
2729 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo);
2730 if (RT_SUCCESS(rc))
2731 rc = rtFsNtfsVolLoadVolumeInfo(pThis, pErrInfo);
2732 if (RT_SUCCESS(rc))
2733 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo);
2734 if (RT_SUCCESS(rc))
2735 rc = rtFsNtfsVolLoadUpCase(pThis, pErrInfo);
2736 if (RT_SUCCESS(rc))
2737 rc = rtFsNtfsVolLoadRootDir(pThis, pErrInfo);
2738 RTMemTmpFree(pvBuf);
2739 if (RT_SUCCESS(rc))
2740 {
2741 *phVfs = hVfs;
2742 return VINF_SUCCESS;
2743 }
2744 }
2745 else
2746 rc = VERR_NO_TMP_MEMORY;
2747 }
2748
2749 RTVfsRelease(hVfs);
2750 *phVfs = NIL_RTVFS;
2751 }
2752 else
2753 RTVfsFileRelease(hVfsFileIn);
2754
2755 return rc;
2756}
2757
2758
2759/**
2760 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2761 */
2762static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2763 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2764{
2765 RT_NOREF(pProviderReg);
2766
2767 /*
2768 * Basic checks.
2769 */
2770 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2771 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2772 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2773 && pElement->enmType != RTVFSOBJTYPE_DIR)
2774 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2775 if (pElement->cArgs > 1)
2776 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2777
2778 /*
2779 * Parse the flag if present, save in pElement->uProvider.
2780 */
2781 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2782 if (pElement->cArgs > 0)
2783 {
2784 const char *psz = pElement->paArgs[0].psz;
2785 if (*psz)
2786 {
2787 if (!strcmp(psz, "ro"))
2788 fReadOnly = true;
2789 else if (!strcmp(psz, "rw"))
2790 fReadOnly = false;
2791 else
2792 {
2793 *poffError = pElement->paArgs[0].offSpec;
2794 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2795 }
2796 }
2797 }
2798
2799 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2800 return VINF_SUCCESS;
2801}
2802
2803
2804/**
2805 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2806 */
2807static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2808 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2809 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2810{
2811 RT_NOREF(pProviderReg, pSpec, poffError);
2812
2813 int rc;
2814 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2815 if (hVfsFileIn != NIL_RTVFSFILE)
2816 {
2817 RTVFS hVfs;
2818 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2819 RTVfsFileRelease(hVfsFileIn);
2820 if (RT_SUCCESS(rc))
2821 {
2822 *phVfsObj = RTVfsObjFromVfs(hVfs);
2823 RTVfsRelease(hVfs);
2824 if (*phVfsObj != NIL_RTVFSOBJ)
2825 return VINF_SUCCESS;
2826 rc = VERR_VFS_CHAIN_CAST_FAILED;
2827 }
2828 }
2829 else
2830 rc = VERR_VFS_CHAIN_CAST_FAILED;
2831 return rc;
2832}
2833
2834
2835/**
2836 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2837 */
2838static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2839 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2840 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2841{
2842 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2843 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2844 || !pReuseElement->paArgs[0].uProvider)
2845 return true;
2846 return false;
2847}
2848
2849
2850/** VFS chain element 'file'. */
2851static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
2852{
2853 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2854 /* fReserved = */ 0,
2855 /* pszName = */ "ntfs",
2856 /* ListEntry = */ { NULL, NULL },
2857 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
2858 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2859 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
2860 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
2861 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
2862 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2863};
2864
2865RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
2866
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