VirtualBox

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

Last change on this file since 100908 was 100908, checked in by vboxsync, 18 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

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