VirtualBox

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

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

iprt/ntfsvfs.cpp: Implemented ReadDir, fixed Open('..') bug.

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