VirtualBox

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

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

iprt/ntfsvfs.cpp: Implement proper resource cleanup on volume close. Added MFT record / core object caching.

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