VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 240.0 KB
Line 
1/* $Id: ntfsvfs.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - NTFS Virtual Filesystem, currently only for reading allocation bitmap.
4 */
5
6/*
7 * Copyright (C) 2012-2022 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, PCRTSGBUF 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 pThis->offFile = off + cbRead;
2394 Log6(("rtFsNtfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
2395 }
2396 else
2397 {
2398 PRTFSNTFSATTR pDataAttr = pThis->pShared->pData;
2399 if ((uint64_t)off >= pDataAttr->cbValue)
2400 {
2401 *pcbRead = 0;
2402 rc = VINF_EOF;
2403 }
2404 else
2405 {
2406 if ((uint64_t)off + cbRead <= pDataAttr->cbValue)
2407 rc = rtFsNtfsAttr_Read(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbRead);
2408 else
2409 {
2410 /* Return VINF_EOF if beyond end-of-file. */
2411 cbRead = (size_t)(pDataAttr->cbValue - (uint64_t)off);
2412 rc = rtFsNtfsAttr_Read(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbRead);
2413 if (RT_SUCCESS(rc))
2414 rc = VINF_EOF;
2415 }
2416 if (RT_SUCCESS(rc))
2417 {
2418 pThis->offFile = off + cbRead;
2419 *pcbRead = cbRead;
2420 }
2421 else
2422 *pcbRead = 0;
2423 }
2424 Log6(("rtFsNtfsFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
2425 }
2426
2427 return rc;
2428}
2429
2430
2431/**
2432 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
2433 */
2434static DECLCALLBACK(int) rtFsNtfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
2435{
2436 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2437 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
2438 RT_NOREF(fBlocking);
2439
2440 if (off == -1)
2441 off = pThis->offFile;
2442 else
2443 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
2444
2445 int rc;
2446 PRTFSNTFSATTR pDataAttr = pThis->pShared->pData;
2447 size_t cbToWrite = pSgBuf->paSegs[0].cbSeg;
2448 if ((uint64_t)off + cbToWrite <= pDataAttr->cbValue)
2449 {
2450 rc = rtFsNtfsAttr_Write(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbToWrite);
2451 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> %Rrc\n", off, cbToWrite, rc));
2452 if (RT_SUCCESS(rc))
2453 pThis->offFile = off + cbToWrite;
2454 if (pcbWritten)
2455 *pcbWritten = RT_SUCCESS(rc) ? cbToWrite : 0;
2456 }
2457 else if ((uint64_t)off < pDataAttr->cbValue)
2458 {
2459 size_t cbWritten = pDataAttr->cbValue - off;
2460 rc = rtFsNtfsAttr_Write(pThis->pShared->pData, off, pSgBuf->paSegs[0].pvSeg, cbWritten);
2461 if (RT_SUCCESS(rc))
2462 {
2463 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> VERR_EOF [EOF: %#RX64, Written: %#zx]\n",
2464 off, cbToWrite, pDataAttr->cbValue, cbWritten));
2465 pThis->offFile = off + cbWritten;
2466 if (pcbWritten)
2467 *pcbWritten = cbWritten;
2468 rc = VERR_EOF;
2469 }
2470 else
2471 {
2472 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> %Rrc [EOF: %#RX64]\n", off, cbToWrite, rc, pDataAttr->cbValue));
2473 if (pcbWritten)
2474 *pcbWritten = 0;
2475 }
2476 }
2477 else
2478 {
2479 Log6(("rtFsNtfsFile_Write: off=%#RX64 cbToWrite=%#zx -> VERR_EOF [EOF: %#RX64]\n", off, cbToWrite, pDataAttr->cbValue));
2480 rc = VERR_EOF;
2481 if (pcbWritten)
2482 *pcbWritten = 0;
2483 }
2484
2485 return rc;
2486}
2487
2488
2489/**
2490 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
2491 */
2492static DECLCALLBACK(int) rtFsNtfsFile_Flush(void *pvThis)
2493{
2494 RT_NOREF(pvThis);
2495 return VINF_SUCCESS;
2496}
2497
2498
2499/**
2500 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
2501 */
2502static DECLCALLBACK(int) rtFsNtfsFile_Tell(void *pvThis, PRTFOFF poffActual)
2503{
2504 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2505 *poffActual = pThis->offFile;
2506 return VINF_SUCCESS;
2507}
2508
2509
2510/**
2511 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2512 */
2513static DECLCALLBACK(int) rtFsNtfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2514{
2515 RT_NOREF(pvThis, fMode, fMask);
2516 return VERR_WRITE_PROTECT;
2517}
2518
2519
2520/**
2521 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2522 */
2523static DECLCALLBACK(int) rtFsNtfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2524 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2525{
2526 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2527 return VERR_WRITE_PROTECT;
2528}
2529
2530
2531/**
2532 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2533 */
2534static DECLCALLBACK(int) rtFsNtfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2535{
2536 RT_NOREF(pvThis, uid, gid);
2537 return VERR_WRITE_PROTECT;
2538}
2539
2540
2541/**
2542 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
2543 */
2544static DECLCALLBACK(int) rtFsNtfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
2545{
2546 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2547 RTFOFF offNew;
2548 switch (uMethod)
2549 {
2550 case RTFILE_SEEK_BEGIN:
2551 offNew = offSeek;
2552 break;
2553 case RTFILE_SEEK_END:
2554 offNew = (RTFOFF)pThis->pShared->pData->cbValue + offSeek;
2555 break;
2556 case RTFILE_SEEK_CURRENT:
2557 offNew = (RTFOFF)pThis->offFile + offSeek;
2558 break;
2559 default:
2560 return VERR_INVALID_PARAMETER;
2561 }
2562 if (offNew >= 0)
2563 {
2564 pThis->offFile = offNew;
2565 *poffActual = offNew;
2566 return VINF_SUCCESS;
2567 }
2568 return VERR_NEGATIVE_SEEK;
2569}
2570
2571
2572/**
2573 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
2574 */
2575static DECLCALLBACK(int) rtFsNtfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
2576{
2577 PRTFSNTFSFILE pThis = (PRTFSNTFSFILE)pvThis;
2578 *pcbFile = pThis->pShared->pData->cbValue;
2579 return VINF_SUCCESS;
2580}
2581
2582
2583/**
2584 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
2585 */
2586static DECLCALLBACK(int) rtFsNtfsFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
2587{
2588 NOREF(pvThis); NOREF(cbFile); NOREF(fFlags);
2589 return VERR_NOT_IMPLEMENTED;
2590}
2591
2592
2593/**
2594 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
2595 */
2596static DECLCALLBACK(int) rtFsNtfsFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
2597{
2598 RT_NOREF(pvThis);
2599 *pcbMax = INT64_MAX;
2600 return VINF_SUCCESS;
2601}
2602
2603
2604/**
2605 * NTFS file operations.
2606 */
2607static const RTVFSFILEOPS g_rtFsNtfsFileOps =
2608{
2609 { /* Stream */
2610 { /* Obj */
2611 RTVFSOBJOPS_VERSION,
2612 RTVFSOBJTYPE_FILE,
2613 "NTFS File",
2614 rtFsNtfsFile_Close,
2615 rtFsNtfsFile_QueryInfo,
2616 NULL,
2617 RTVFSOBJOPS_VERSION
2618 },
2619 RTVFSIOSTREAMOPS_VERSION,
2620 RTVFSIOSTREAMOPS_FEAT_NO_SG,
2621 rtFsNtfsFile_Read,
2622 rtFsNtfsFile_Write,
2623 rtFsNtfsFile_Flush,
2624 NULL /*PollOne*/,
2625 rtFsNtfsFile_Tell,
2626 NULL /*pfnSkip*/,
2627 NULL /*pfnZeroFill*/,
2628 RTVFSIOSTREAMOPS_VERSION,
2629 },
2630 RTVFSFILEOPS_VERSION,
2631 0,
2632 { /* ObjSet */
2633 RTVFSOBJSETOPS_VERSION,
2634 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
2635 rtFsNtfsFile_SetMode,
2636 rtFsNtfsFile_SetTimes,
2637 rtFsNtfsFile_SetOwner,
2638 RTVFSOBJSETOPS_VERSION
2639 },
2640 rtFsNtfsFile_Seek,
2641 rtFsNtfsFile_QuerySize,
2642 rtFsNtfsFile_SetSize,
2643 rtFsNtfsFile_QueryMaxSize,
2644 RTVFSFILEOPS_VERSION
2645};
2646
2647
2648static int rtFsNtfsVol_NewFile(PRTFSNTFSVOL pThis, uint64_t fOpen, PCNTFSIDXENTRYHDR pEntryHdr, const char *pszStreamName,
2649 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
2650{
2651 /*
2652 * Get the core structure for the MFT record and check that it's a directory we've got.
2653 */
2654 PRTFSNTFSCORE pCore;
2655 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, &pEntryHdr->u.FileMftRec, false /*fRelaxedUsa*/, &pCore, pErrInfo);
2656 if (RT_SUCCESS(rc))
2657 {
2658 if (!(pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY))
2659 {
2660 /*
2661 * Locate the data attribute.
2662 */
2663 PRTFSNTFSATTR pDataAttr;
2664 if (pszStreamName == NULL)
2665 {
2666 pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
2667 if (pDataAttr)
2668 rc = VINF_SUCCESS;
2669 else
2670 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: no unamed data stream", pszWhat);
2671 }
2672 else
2673 {
2674 NOREF(pszStreamName);
2675 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_IMPLEMENTED, "%s: named data streams not implemented yet", pszWhat);
2676 pDataAttr = NULL;
2677 }
2678 if (RT_SUCCESS(rc))
2679 {
2680 /*
2681 * Get a referenced shared file structure, creating it if necessary.
2682 */
2683 PRTFSNTFSFILESHRD pShared = pDataAttr->uObj.pSharedFile;
2684 if (pShared)
2685 {
2686 uint32_t cRefs = ASMAtomicIncU32(&pShared->cRefs);
2687 Assert(cRefs > 1); NOREF(cRefs);
2688 }
2689 else
2690 {
2691 pShared = (PRTFSNTFSFILESHRD)RTMemAllocZ(sizeof(*pShared));
2692 if (pShared)
2693 {
2694 pShared->cRefs = 1;
2695 pShared->pData = pDataAttr;
2696 rtFsNtfsCore_Retain(pCore);
2697 pDataAttr->uObj.pSharedFile = pShared;
2698 }
2699 }
2700 if (pShared)
2701 {
2702 /*
2703 * Create the open file instance.
2704 */
2705 PRTFSNTFSFILE pNewFile;
2706 rc = RTVfsNewFile(&g_rtFsNtfsFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
2707 phVfsFile, (void **)&pNewFile);
2708 if (RT_SUCCESS(rc))
2709 {
2710 pNewFile->offFile = 0;
2711 pNewFile->pShared = pShared;
2712 rtFsNtfsCore_Release(pCore);
2713 return VINF_SUCCESS;
2714 }
2715
2716 rtFsNtfsFileShrd_Release(pShared);
2717 }
2718 else
2719 rc = VERR_NO_MEMORY;
2720 }
2721 }
2722 else
2723 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
2724 rtFsNtfsCore_Release(pCore);
2725 }
2726 return rc;
2727}
2728
2729
2730
2731/*
2732 *
2733 * NTFS directory code.
2734 * NTFS directory code.
2735 * NTFS directory code.
2736 *
2737 */
2738
2739#ifdef LOG_ENABLED
2740
2741/**
2742 * Logs an index header and all the entries.
2743 *
2744 * @param pIdxHdr The index header.
2745 * @param cbIndex The number of valid bytes starting with the header.
2746 * @param offIndex The offset of the index header into the parent
2747 * structure.
2748 * @param pszPrefix The log prefix.
2749 * @param uIdxType The index type.
2750 */
2751static void rtFsNtfsVol_LogIndexHdrAndEntries(PCNTFSINDEXHDR pIdxHdr, uint32_t cbIndex, uint32_t offIndex,
2752 const char *pszPrefix, uint32_t uIdxType)
2753{
2754 if (!LogIs2Enabled())
2755 return;
2756
2757 /*
2758 * Do the header.
2759 */
2760 if (cbIndex <= sizeof(*pIdxHdr))
2761 {
2762 Log2(("NTFS: %s: Error! Not enough space for the index header! cbIndex=%#x, index head needs %#x\n",
2763 pszPrefix, cbIndex, sizeof(*pIdxHdr)));
2764 return;
2765 }
2766
2767 Log2(("NTFS: %s: offFirstEntry %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->offFirstEntry),
2768 RT_LE2H_U32(pIdxHdr->offFirstEntry) >= cbIndex ? " !out-of-bounds!" : ""));
2769 Log2(("NTFS: %s: cbUsed %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbUsed),
2770 RT_LE2H_U32(pIdxHdr->cbUsed) > cbIndex ? " !out-of-bounds!" : ""));
2771 Log2(("NTFS: %s: cbAllocated %#x%s\n", pszPrefix, RT_LE2H_U32(pIdxHdr->cbAllocated),
2772 RT_LE2H_U32(pIdxHdr->cbAllocated) > cbIndex ? " !out-of-bounds!" : ""));
2773 Log2(("NTFS: %s: fFlags %#x (%s%s)\n", pszPrefix, pIdxHdr->fFlags,
2774 pIdxHdr->fFlags & NTFSINDEXHDR_F_INTERNAL ? "internal" : "leaf",
2775 pIdxHdr->fFlags & ~NTFSINDEXHDR_F_INTERNAL ? " !!unknown-flags!!" : ""));
2776 if (pIdxHdr->abReserved[0]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[0]));
2777 if (pIdxHdr->abReserved[1]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[1]));
2778 if (pIdxHdr->abReserved[2]) Log2(("NTFS: %s: abReserved[0] %#x\n", pszPrefix, pIdxHdr->abReserved[2]));
2779
2780 /*
2781 * The entries.
2782 */
2783 bool fSeenEnd = false;
2784 uint32_t iEntry = 0;
2785 uint32_t offCurEntry = RT_LE2H_U32(pIdxHdr->offFirstEntry);
2786 while (offCurEntry < cbIndex)
2787 {
2788 if (offCurEntry + sizeof(NTFSIDXENTRYHDR) > cbIndex)
2789 {
2790 Log2(("NTFS: Entry[%#04x]: Out of bounds: %#x LB %#x, max %#x\n",
2791 iEntry, offCurEntry, sizeof(NTFSIDXENTRYHDR), cbIndex));
2792 break;
2793 }
2794 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIdxHdr + offCurEntry);
2795 Log2(("NTFS: [%#04x]: @%#05x/@%#05x cbEntry=%#x cbKey=%#x fFlags=%#x (%s%s%s)\n",
2796 iEntry, offCurEntry, offCurEntry + offIndex, RT_LE2H_U16(pEntryHdr->cbEntry), RT_LE2H_U16(pEntryHdr->cbKey),
2797 RT_LE2H_U16(pEntryHdr->fFlags),
2798 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? "internal" : "leaf",
2799 pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END ? " end" : "",
2800 pEntryHdr->fFlags & ~(NTFSIDXENTRYHDR_F_INTERNAL | NTFSIDXENTRYHDR_F_END) ? " !unknown!" : ""));
2801 if (uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2802 Log2(("NTFS: FileMftRec %#RX64 sqn %#x\n",
2803 NTFSMFTREF_GET_IDX(&pEntryHdr->u.FileMftRec), NTFSMFTREF_GET_SEQ(&pEntryHdr->u.FileMftRec) ));
2804 else
2805 Log2(("NTFS: offData=%#x cbData=%#x uReserved=%#x\n",
2806 RT_LE2H_U16(pEntryHdr->u.View.offData), RT_LE2H_U16(pEntryHdr->u.View.cbData),
2807 RT_LE2H_U32(pEntryHdr->u.View.uReserved) ));
2808 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
2809 Log2(("NTFS: Subnode=%#RX64\n", RT_LE2H_U64(NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr)) ));
2810
2811 if ( RT_LE2H_U16(pEntryHdr->cbKey) >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename)
2812 && uIdxType == NTFSATINDEXROOT_TYPE_DIR)
2813 {
2814 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2815 RTTIMESPEC Spec;
2816 char sz[80];
2817 Log2(("NTFS: iCreationTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iCreationTime),
2818 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iCreationTime)), sz, sizeof(sz)) ));
2819 Log2(("NTFS: iLastDataModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastDataModTime),
2820 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastDataModTime)), sz, sizeof(sz)) ));
2821 Log2(("NTFS: iLastMftModTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastMftModTime),
2822 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastMftModTime)), sz, sizeof(sz)) ));
2823 Log2(("NTFS: iLastAccessTime %#RX64 %s\n", RT_LE2H_U64(pFilename->iLastAccessTime),
2824 RTTimeSpecToString(RTTimeSpecSetNtTime(&Spec, RT_LE2H_U64(pFilename->iLastAccessTime)), sz, sizeof(sz)) ));
2825 Log2(("NTFS: cbAllocated %#RX64 (%Rhcb)\n",
2826 RT_LE2H_U64(pFilename->cbAllocated), RT_LE2H_U64(pFilename->cbAllocated)));
2827 Log2(("NTFS: cbData %#RX64 (%Rhcb)\n",
2828 RT_LE2H_U64(pFilename->cbData), RT_LE2H_U64(pFilename->cbData)));
2829 Log2(("NTFS: fFileAttribs %#RX32\n", RT_LE2H_U32(pFilename->fFileAttribs) ));
2830 if (RT_LE2H_U32(pFilename->fFileAttribs) & NTFS_FA_REPARSE_POINT)
2831 Log2(("NTFS: uReparseTag %#RX32\n", RT_LE2H_U32(pFilename->u.uReparseTag) ));
2832 else
2833 Log2(("NTFS: cbPackedEas %#RX16\n", RT_LE2H_U16(pFilename->u.cbPackedEas) ));
2834 Log2(("NTFS: cwcFilename %#x\n", pFilename->cwcFilename));
2835 Log2(("NTFS: fFilenameType %#x\n", pFilename->fFilenameType));
2836 if (RT_UOFFSETOF_DYN(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) <= RT_LE2H_U16(pEntryHdr->cbKey))
2837 Log2(("NTFS: wszFilename '%.*ls'\n", pFilename->cwcFilename, pFilename->wszFilename ));
2838 else
2839 Log2(("NTFS: Error! Truncated filename!!\n"));
2840 }
2841
2842
2843 /* next */
2844 iEntry++;
2845 offCurEntry += RT_LE2H_U16(pEntryHdr->cbEntry);
2846 fSeenEnd = RT_BOOL(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END);
2847 if (fSeenEnd || RT_LE2H_U16(pEntryHdr->cbEntry) < sizeof(*pEntryHdr))
2848 break;
2849 }
2850 if (!fSeenEnd)
2851 Log2(("NTFS: %s: Warning! Missing NTFSIDXENTRYHDR_F_END node!\n", pszPrefix));
2852}
2853
2854# if 0 /* unused */
2855static void rtFsNtfsVol_LogIndexNode(PCNTFSATINDEXALLOC pIdxNode, uint32_t cbIdxNode, uint32_t uType)
2856{
2857 if (!LogIs2Enabled())
2858 return;
2859 if (cbIdxNode < sizeof(*pIdxNode))
2860 Log2(("NTFS: Index Node: Error! Too small! cbIdxNode=%#x, index node needs %#x\n", cbIdxNode, sizeof(*pIdxNode)));
2861 else
2862 {
2863 Log2(("NTFS: Index Node: uMagic %#x\n", RT_LE2H_U32(pIdxNode->RecHdr.uMagic)));
2864 Log2(("NTFS: Index Node: UpdateSeqArray %#x L %#x\n",
2865 RT_LE2H_U16(pIdxNode->RecHdr.offUpdateSeqArray), RT_LE2H_U16(pIdxNode->RecHdr.cUpdateSeqEntries) ));
2866 Log2(("NTFS: Index Node: uLsn %#RX64\n", RT_LE2H_U64(pIdxNode->uLsn) ));
2867 Log2(("NTFS: Index Node: iSelfAddress %#RX64\n", RT_LE2H_U64(pIdxNode->iSelfAddress) ));
2868 if (pIdxNode->RecHdr.uMagic == NTFSREC_MAGIC_INDEX_ALLOC)
2869 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxNode->Hdr, cbIdxNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
2870 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "Index Node Hdr", uType);
2871 else
2872 Log2(("NTFS: Index Node: !Error! Invalid magic!\n"));
2873 }
2874}
2875# endif
2876
2877/**
2878 * Logs a index root structure and what follows (index header + entries).
2879 *
2880 * @param pIdxRoot The index root.
2881 * @param cbIdxRoot Number of valid bytes starting with @a pIdxRoot.
2882 */
2883static void rtFsNtfsVol_LogIndexRoot(PCNTFSATINDEXROOT pIdxRoot, uint32_t cbIdxRoot)
2884{
2885 if (!LogIs2Enabled())
2886 return;
2887 if (cbIdxRoot < sizeof(*pIdxRoot))
2888 Log2(("NTFS: Index Root: Error! Too small! cbIndex=%#x, index head needs %#x\n", cbIdxRoot, sizeof(*pIdxRoot)));
2889 else
2890 {
2891 Log2(("NTFS: Index Root: cbIdxRoot %#x\n", cbIdxRoot));
2892 Log2(("NTFS: Index Root: uType %#x %s\n", RT_LE2H_U32(pIdxRoot->uType),
2893 pIdxRoot->uType == NTFSATINDEXROOT_TYPE_VIEW ? "view"
2894 : pIdxRoot->uType == NTFSATINDEXROOT_TYPE_DIR ? "directory" : "!unknown!"));
2895 Log2(("NTFS: Index Root: uCollationRules %#x %s\n", RT_LE2H_U32(pIdxRoot->uCollationRules),
2896 pIdxRoot->uCollationRules == NTFS_COLLATION_BINARY ? "binary"
2897 : pIdxRoot->uCollationRules == NTFS_COLLATION_FILENAME ? "filename"
2898 : pIdxRoot->uCollationRules == NTFS_COLLATION_UNICODE_STRING ? "unicode-string"
2899 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32 ? "uint32"
2900 : pIdxRoot->uCollationRules == NTFS_COLLATION_SID ? "sid"
2901 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_PAIR ? "uint32-pair"
2902 : pIdxRoot->uCollationRules == NTFS_COLLATION_UINT32_SEQ ? "uint32-sequence" : "!unknown!"));
2903 Log2(("NTFS: Index Root: cbIndexNode %#x\n", RT_LE2H_U32(pIdxRoot->cbIndexNode) ));
2904 Log2(("NTFS: Index Root: cAddressesPerIndexNode %#x => cbNodeAddressingUnit=%#x\n",
2905 pIdxRoot->cAddressesPerIndexNode, RT_LE2H_U32(pIdxRoot->cbIndexNode) / RT_MAX(1, pIdxRoot->cAddressesPerIndexNode) ));
2906 if (pIdxRoot->abReserved[0]) Log2(("NTFS: Index Root: abReserved[0] %#x\n", pIdxRoot->abReserved[0]));
2907 if (pIdxRoot->abReserved[1]) Log2(("NTFS: Index Root: abReserved[1] %#x\n", pIdxRoot->abReserved[1]));
2908 if (pIdxRoot->abReserved[2]) Log2(("NTFS: Index Root: abReserved[2] %#x\n", pIdxRoot->abReserved[2]));
2909
2910 rtFsNtfsVol_LogIndexHdrAndEntries(&pIdxRoot->Hdr, cbIdxRoot - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr),
2911 RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), "Index Root Hdr", pIdxRoot->uType);
2912 }
2913}
2914
2915#endif /* LOG_ENABLED */
2916
2917
2918/**
2919 * Validates an index header.
2920 *
2921 * @returns IPRT status code.
2922 * @param pRootInfo Pointer to the index root info.
2923 * @param pNodeInfo Pointer to the node info structure to load.
2924 * @param pIndexHdr Pointer to the index header.
2925 * @param cbIndex Size of the index.
2926 * @param pErrInfo Where to return extra error info.
2927 * @param pszWhat Error prefix.
2928 */
2929static int rtFsNtfsVol_LoadIndexNodeInfo(PCRTFSNTFSIDXROOTINFO pRootInfo, PRTFSNTFSIDXNODEINFO pNodeInfo, PCNTFSINDEXHDR pIndexHdr,
2930 uint32_t cbIndex, PRTERRINFO pErrInfo, const char *pszWhat)
2931{
2932 uint32_t const cbMinIndex = sizeof(*pIndexHdr) + sizeof(NTFSIDXENTRYHDR);
2933 if (cbIndex < cbMinIndex)
2934 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2935 "%s: Not enough room for the index header and one entry header! cbIndex=%#x (cbMinIndex=%#x)",
2936 pszWhat, cbIndex, cbMinIndex);
2937 uint32_t const cbAllocated = RT_LE2H_U32(pIndexHdr->cbAllocated);
2938 if ( cbAllocated > cbIndex
2939 || cbAllocated < cbMinIndex
2940 || (cbAllocated & 7) )
2941 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2942 "%s: Bogus index allocation size: %#x (min %#x, max %#x, 8 byte aligned)",
2943 pszWhat, cbAllocated, cbMinIndex, cbIndex);
2944 uint32_t const cbUsed = RT_LE2H_U32(pIndexHdr->cbUsed);
2945 if ( cbUsed > cbAllocated
2946 || cbUsed < cbMinIndex
2947 || (cbUsed & 7) )
2948 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2949 "%s: Bogus index used size: %#x (min %#x, max %#x, 8 byte aligned)",
2950 pszWhat, cbUsed, cbMinIndex, cbAllocated);
2951 uint32_t const offFirstEntry = RT_LE2H_U32(pIndexHdr->offFirstEntry);
2952 if ( offFirstEntry < sizeof(*pIndexHdr)
2953 || ( offFirstEntry > cbUsed - sizeof(NTFSIDXENTRYHDR)
2954 && offFirstEntry != cbUsed /* empty dir */)
2955 || (offFirstEntry & 7) )
2956 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2957 "%s: Bogus first entry offset: %#x (min %#x, max %#x, 8 byte aligned)",
2958 pszWhat, offFirstEntry, sizeof(*pIndexHdr), cbUsed - sizeof(NTFSIDXENTRYHDR));
2959
2960 /*
2961 * The index entries.
2962 */
2963 uint32_t const uType = pRootInfo->pRoot->uType;
2964 uint32_t offEntry = offFirstEntry;
2965 uint32_t iEntry = 0;
2966 for (;;)
2967 {
2968 if (offEntry + sizeof(NTFSIDXENTRYHDR) > cbUsed)
2969 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2970 "%s: Entry #%u is out of bound: offset %#x (cbUsed=%#x)",
2971 pszWhat, iEntry, offEntry, cbUsed);
2972 PCNTFSIDXENTRYHDR pEntryHdr = (PCNTFSIDXENTRYHDR)((uint8_t const *)pIndexHdr + offEntry);
2973 uint16_t const cbEntry = RT_LE2H_U16(pEntryHdr->cbEntry);
2974 uint32_t const cbSubnodeAddr = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL ? sizeof(int64_t) : 0);
2975 uint32_t const cbMinEntry = sizeof(*pEntryHdr) + cbSubnodeAddr;
2976 if ( cbEntry < cbMinEntry
2977 || offEntry + cbEntry > cbUsed
2978 || (cbEntry & 7) )
2979 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2980 "%s: Entry #%u has a bogus size: %#x (min %#x, max %#x, 8 byte aligned)",
2981 pszWhat, iEntry, cbEntry, cbMinEntry, cbUsed - offEntry);
2982
2983 uint32_t const cbMaxKey = cbEntry - sizeof(*pEntryHdr) - cbSubnodeAddr;
2984 uint32_t const cbMinKey = (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END) ? 0
2985 : uType == NTFSATINDEXROOT_TYPE_DIR ? RT_UOFFSETOF(NTFSATFILENAME, wszFilename) : 0;
2986 uint16_t const cbKey = RT_LE2H_U16(pEntryHdr->cbKey);
2987 if ( cbKey < cbMinKey
2988 || cbKey > cbMaxKey)
2989 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2990 "%s: Entry #%u has a bogus key size: %#x (min %#x, max %#x)",
2991 pszWhat, iEntry, cbKey, cbMinKey, cbMaxKey);
2992 if ( !(pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
2993 && uType == NTFSATINDEXROOT_TYPE_DIR)
2994 {
2995 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntryHdr + 1);
2996 if (RT_UOFFSETOF_DYN(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]) > cbKey)
2997 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
2998 "%s: Entry #%u filename is out of bounds: cwcFilename=%#x -> %#x key, max %#x",
2999 pszWhat, iEntry, pFilename->cwcFilename,
3000 RT_UOFFSETOF_DYN(NTFSATFILENAME, wszFilename[pFilename->cwcFilename]), cbKey);
3001 }
3002
3003 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
3004 {
3005 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntryHdr);
3006 if ( (uint64_t)iSubnode >= pRootInfo->uEndNodeAddresses
3007 || (iSubnode & pRootInfo->fNodeAddressMisalign) )
3008 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3009 "%s: Entry #%u has bogus subnode address: %#RX64 (max %#RX64, misalign %#x)",
3010 pszWhat, iEntry, iSubnode, pRootInfo->uEndNodeAddresses,
3011 pRootInfo->fNodeAddressMisalign);
3012 }
3013
3014 /* Advance. */
3015 offEntry += cbEntry;
3016 iEntry++;
3017 if (pEntryHdr->fFlags & NTFSIDXENTRYHDR_F_END)
3018 break;
3019 }
3020
3021 /*
3022 * Popuplate the node info structure.
3023 */
3024 pNodeInfo->pIndexHdr = pIndexHdr;
3025 pNodeInfo->fInternal = RT_BOOL(pIndexHdr->fFlags & NTFSINDEXHDR_F_INTERNAL);
3026 if (pNodeInfo != &pRootInfo->NodeInfo)
3027 pNodeInfo->pVol = pRootInfo->NodeInfo.pVol;
3028 pNodeInfo->cEntries = iEntry;
3029 pNodeInfo->papEntries = (PCNTFSIDXENTRYHDR *)RTMemAlloc(iEntry * sizeof(pNodeInfo->papEntries[0]));
3030 if (pNodeInfo->papEntries)
3031 {
3032 PCNTFSIDXENTRYHDR pEntryHdr = NTFSINDEXHDR_GET_FIRST_ENTRY(pIndexHdr);
3033 for (iEntry = 0; iEntry < pNodeInfo->cEntries; iEntry++)
3034 {
3035 pNodeInfo->papEntries[iEntry] = pEntryHdr;
3036 pEntryHdr = NTFSIDXENTRYHDR_GET_NEXT(pEntryHdr);
3037 }
3038 return VINF_SUCCESS;
3039 }
3040 return VERR_NO_MEMORY;
3041}
3042
3043
3044/**
3045 * Creates a shared directory structure given a MFT core.
3046 *
3047 * @returns IPRT status code.
3048 * @param pThis The NTFS volume instance.
3049 * @param pCore The MFT core structure that's allegedly a directory.
3050 * (No reference consumed of course.)
3051 * @param ppSharedDir Where to return the pointer to the new shared directory
3052 * structure on success. (Referenced.)
3053 * @param pErrInfo Where to return additions error info. Optional.
3054 * @param pszWhat Context prefix for error reporting and logging.
3055 */
3056static int rtFsNtfsVol_NewSharedDirFromCore(PRTFSNTFSVOL pThis, PRTFSNTFSCORE pCore, PRTFSNTFSDIRSHRD *ppSharedDir,
3057 PRTERRINFO pErrInfo, const char *pszWhat)
3058{
3059 *ppSharedDir = NULL;
3060
3061 /*
3062 * Look for the index root and validate it.
3063 */
3064 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3065 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3066 if (!pRootAttr)
3067 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: Found no INDEX_ROOT attribute named $I30", pszWhat);
3068 if (pRootAttr->pAttrHdr->fNonResident)
3069 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is is not resident", pszWhat);
3070 if (pRootAttr->cbResident < sizeof(NTFSATINDEXROOT))
3071 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ROOT is too small: %#x, min %#x ",
3072 pszWhat, pRootAttr->cbResident, sizeof(pRootAttr->cbResident));
3073
3074 PCNTFSATINDEXROOT pIdxRoot = (PCNTFSATINDEXROOT)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pRootAttr->pAttrHdr);
3075#ifdef LOG_ENABLED
3076 rtFsNtfsVol_LogIndexRoot(pIdxRoot, pRootAttr->cbResident);
3077#endif
3078 if (pIdxRoot->uType != NTFSATINDEXROOT_TYPE_DIR)
3079 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3080 "%s: Wrong INDEX_ROOT type for a directory: %#x, expected %#x",
3081 pszWhat, RT_LE2H_U32(pIdxRoot->uType), RT_LE2H_U32_C(NTFSATINDEXROOT_TYPE_DIR));
3082 if (pIdxRoot->uCollationRules != NTFS_COLLATION_FILENAME)
3083 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3084 "%s: Wrong collation rules for a directory: %#x, expected %#x",
3085 pszWhat, RT_LE2H_U32(pIdxRoot->uCollationRules), RT_LE2H_U32_C(NTFS_COLLATION_FILENAME));
3086 uint32_t cbIndexNode = RT_LE2H_U32(pIdxRoot->cbIndexNode);
3087 if (cbIndexNode < 512 || cbIndexNode > _64K || !RT_IS_POWER_OF_TWO(cbIndexNode))
3088 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3089 "%s: Bogus index node size: %#x (expected power of two between 512 and 64KB)",
3090 pszWhat, cbIndexNode);
3091 unsigned const cNodeAddressShift = cbIndexNode >= pThis->cbCluster ? pThis->cClusterShift : 9;
3092 if (((uint32_t)pIdxRoot->cAddressesPerIndexNode << cNodeAddressShift) != cbIndexNode)
3093 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3094 "%s: Bogus addresses per index node value: %#x (cbIndexNode=%#x cNodeAddressShift=%#x)",
3095 pszWhat, pIdxRoot->cAddressesPerIndexNode, cbIndexNode, cNodeAddressShift);
3096 AssertReturn(pRootAttr->uObj.pSharedDir == NULL, VERR_INTERNAL_ERROR_3);
3097
3098 /*
3099 * Check for the node data stream and related allocation bitmap.
3100 */
3101 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
3102 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3103 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
3104 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3105 if (pIndexAlloc && !pIndexBitmap)
3106 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3107 "%s: INDEX_ALLOCATION attribute without BITMAP", pszWhat);
3108 if (!pIndexAlloc && pIndexBitmap)
3109 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3110 "%s: BITMAP attribute without INDEX_ALLOCATION", pszWhat);
3111 uint64_t uNodeAddressEnd = 0;
3112 if (pIndexAlloc)
3113 {
3114 if (!pIndexAlloc->pAttrHdr->fNonResident)
3115 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT, "%s: INDEX_ALLOCATION is resident", pszWhat);
3116 if (pIndexAlloc->cbValue & (cbIndexNode - 1))
3117 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3118 "%s: INDEX_ALLOCATION size isn't aligned on node boundrary: %#RX64, cbIndexNode=%#x",
3119 pszWhat, pIndexAlloc->cbValue, cbIndexNode);
3120 uint64_t const cNodes = pIndexAlloc->cbValue / cbIndexNode;
3121 if (pIndexBitmap->cbValue < (RT_ALIGN_64(cNodes, 64) >> 3))
3122 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
3123 "%s: BITMAP size does not match INDEX_ALLOCATION: %#RX64, expected min %#RX64 (cbIndexNode=%#x, cNodes=%#RX64)",
3124 pszWhat, pIndexBitmap->cbValue, RT_ALIGN_64(cNodes, 64) >> 3, cbIndexNode, cNodes);
3125 uNodeAddressEnd = cNodes * pIdxRoot->cAddressesPerIndexNode;
3126 }
3127
3128 /*
3129 * Create a directory instance.
3130 */
3131 PRTFSNTFSDIRSHRD pNewDir = (PRTFSNTFSDIRSHRD)RTMemAllocZ(sizeof(*pNewDir));
3132 if (!pNewDir)
3133 return VERR_NO_MEMORY;
3134
3135 pNewDir->cRefs = 1;
3136 rtFsNtfsCore_Retain(pCore);
3137 pNewDir->RootInfo.pRootAttr = pRootAttr;
3138 pNewDir->RootInfo.pRoot = pIdxRoot;
3139 pNewDir->RootInfo.pAlloc = pIndexAlloc;
3140 pNewDir->RootInfo.uEndNodeAddresses = uNodeAddressEnd;
3141 pNewDir->RootInfo.cNodeAddressByteShift = cNodeAddressShift;
3142 pNewDir->RootInfo.fNodeAddressMisalign = pIdxRoot->cAddressesPerIndexNode - 1;
3143 pNewDir->RootInfo.NodeInfo.pVol = pThis;
3144
3145 /*
3146 * Finally validate the index header and entries.
3147 */
3148 int rc = rtFsNtfsVol_LoadIndexNodeInfo(&pNewDir->RootInfo, &pNewDir->RootInfo.NodeInfo, &pIdxRoot->Hdr,
3149 pRootAttr->cbResident - RT_UOFFSETOF(NTFSATINDEXROOT, Hdr), pErrInfo, pszWhat);
3150 if (RT_SUCCESS(rc))
3151 {
3152 *ppSharedDir = pNewDir;
3153 pRootAttr->uObj.pSharedDir = pNewDir;
3154 return VINF_SUCCESS;
3155 }
3156 RTMemFree(pNewDir);
3157 rtFsNtfsCore_Release(pCore);
3158 return rc;
3159}
3160
3161
3162/**
3163 * Gets a shared directory structure given an MFT record reference, creating a
3164 * new one if necessary.
3165 *
3166 * @returns IPRT status code.
3167 * @param pThis The NTFS volume instance.
3168 * @param pDirMftRef The MFT record reference to follow.
3169 * @param ppSharedDir Where to return the shared directory structure
3170 * (referenced).
3171 * @param pErrInfo Where to return error details. Optional.
3172 * @param pszWhat Error/log prefix.
3173 */
3174static int rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(PRTFSNTFSVOL pThis, PCNTFSMFTREF pDirMftRef,
3175 PRTFSNTFSDIRSHRD *ppSharedDir, PRTERRINFO pErrInfo, const char *pszWhat)
3176{
3177 /*
3178 * Get the core structure for the MFT record and check that it's a directory we've got.
3179 */
3180 PRTFSNTFSCORE pCore;
3181 int rc = rtFsNtfsVol_QueryCoreForMftRef(pThis, pDirMftRef, false /*fRelaxedUsa*/, &pCore, pErrInfo);
3182 if (RT_SUCCESS(rc))
3183 {
3184 if (pCore->pMftRec->pFileRec->fFlags & NTFSRECFILE_F_DIRECTORY)
3185 {
3186 /*
3187 * Locate the $I30 root index attribute as we associate the
3188 * pointer to the shared directory pointer with it.
3189 */
3190 PRTFSNTFSATTR pRootAttr = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
3191 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
3192 if (pRootAttr)
3193 {
3194 if (!pRootAttr->uObj.pSharedDir)
3195 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, ppSharedDir, pErrInfo, pszWhat);
3196 else
3197 {
3198 Assert(pRootAttr->uObj.pSharedDir->RootInfo.pRootAttr->pCore == pCore);
3199 rtFsNtfsDirShrd_Retain(pRootAttr->uObj.pSharedDir);
3200 *ppSharedDir = pRootAttr->uObj.pSharedDir;
3201 }
3202 }
3203 else
3204 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY,
3205 "%s: Found INDEX_ROOT attribute named $I30, even though NTFSRECFILE_F_DIRECTORY is set",
3206 pszWhat);
3207 }
3208 else
3209 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_DIRECTORY, "%s: fFlags=%#x", pszWhat, pCore->pMftRec->pFileRec->fFlags);
3210 rtFsNtfsCore_Release(pCore);
3211 }
3212 return rc;
3213}
3214
3215
3216/**
3217 * Frees resource kept by an index node info structure.
3218 *
3219 * @param pNodeInfo The index node info structure to delelte.
3220 */
3221static void rtFsNtfsIdxNodeInfo_Delete(PRTFSNTFSIDXNODEINFO pNodeInfo)
3222{
3223 RTMemFree(pNodeInfo->papEntries);
3224 pNodeInfo->papEntries = NULL;
3225 pNodeInfo->pNode = NULL;
3226 pNodeInfo->pVol = NULL;
3227}
3228
3229
3230/**
3231 * Gets or loads the specified subnode.
3232 *
3233 * @returns IPRT status code.
3234 * @param pRootInfo The index root info.
3235 * @param iNode The address of the node being queried.
3236 * @param ppNode Where to return the referenced pointer to the node.
3237 */
3238static int rtFsNtfsIdxRootInfo_QueryNode(PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iNode, PRTFSNTFSIDXNODE *ppNode)
3239{
3240 PRTFSNTFSVOL pVol = pRootInfo->NodeInfo.pVol;
3241
3242 /*
3243 * A bit of paranoia. These has been checked already when loading, but it
3244 * usually doesn't hurt too much to be careful.
3245 */
3246 AssertReturn(!(iNode & pRootInfo->fNodeAddressMisalign), VERR_VFS_BOGUS_OFFSET);
3247 AssertReturn((uint64_t)iNode < pRootInfo->uEndNodeAddresses, VERR_VFS_BOGUS_OFFSET);
3248 AssertReturn(pRootInfo->pAlloc, VERR_VFS_BOGUS_OFFSET);
3249
3250 /*
3251 * First translate the node address to a disk byte offset and check the index node cache.
3252 */
3253 uint64_t offNode = iNode << pRootInfo->cNodeAddressByteShift;
3254 uint64_t offNodeOnDisk = rtFsNtfsAttr_OffsetToDisk(pRootInfo->pAlloc, offNode, NULL);
3255 PRTFSNTFSIDXNODE pNode = (PRTFSNTFSIDXNODE)RTAvlU64Get(&pVol->IdxNodeCacheRoot, offNodeOnDisk);
3256 if (pNode)
3257 {
3258 rtFsNtfsIdxNode_Retain(pNode);
3259 *ppNode = pNode;
3260 return VINF_SUCCESS;
3261 }
3262
3263 /*
3264 * Need to create a load a new node.
3265 */
3266 pNode = (PRTFSNTFSIDXNODE)RTMemAllocZ(sizeof(*pNode));
3267 AssertReturn(pNode, VERR_NO_MEMORY);
3268
3269 pNode->TreeNode.Key = offNodeOnDisk;
3270 uint32_t cbIndexNode = RT_LE2H_U32(pRootInfo->pRoot->cbIndexNode);
3271 pNode->cbCost = sizeof(*pNode) + cbIndexNode;
3272 pNode->cRefs = 1;
3273 pNode->pNode = (PNTFSATINDEXALLOC)RTMemAllocZ(cbIndexNode);
3274 int rc;
3275 if (pNode->pNode)
3276 {
3277 rc = rtFsNtfsAttr_Read(pRootInfo->pAlloc, offNode, pNode->pNode, cbIndexNode);
3278 if (RT_SUCCESS(rc))
3279 {
3280 rc = VERR_VFS_BOGUS_FORMAT;
3281 if (pNode->pNode->RecHdr.uMagic != NTFSREC_MAGIC_INDEX_ALLOC)
3282 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Invalid node magic %#x -> VERR_VFS_BOGUS_FORMAT\n",
3283 iNode, RT_LE2H_U32(pNode->pNode->RecHdr.uMagic) ));
3284 else if ((int64_t)RT_LE2H_U64(pNode->pNode->iSelfAddress) != iNode)
3285 LogRel(("rtFsNtfsIdxRootInfo_QueryNode(iNode=%#x): Wrong iSelfAddress: %#x -> VERR_VFS_BOGUS_FORMAT\n",
3286 iNode, RT_LE2H_U64(pNode->pNode->iSelfAddress) ));
3287 else
3288 {
3289 rc = rtFsNtfsRec_DoMultiSectorFixups(&pNode->pNode->RecHdr, cbIndexNode, false /*fRelaxedUsa*/, NULL /*pErrInfo*/);
3290 if (RT_SUCCESS(rc))
3291 {
3292 /*
3293 * Validate/parse it
3294 */
3295#ifdef LOG_ENABLED
3296 rtFsNtfsVol_LogIndexHdrAndEntries(&pNode->pNode->Hdr,
3297 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
3298 RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr), "index node",
3299 pRootInfo->pRoot->uType);
3300#endif
3301 rc = rtFsNtfsVol_LoadIndexNodeInfo(pRootInfo, &pNode->NodeInfo, &pNode->pNode->Hdr,
3302 cbIndexNode - RT_UOFFSETOF(NTFSATINDEXALLOC, Hdr),
3303 NULL /*pErrInfo*/, "index node");
3304 if (RT_SUCCESS(rc))
3305 {
3306 pNode->cbCost += pNode->NodeInfo.cEntries * sizeof(pNode->NodeInfo.papEntries[0]);
3307
3308 /*
3309 * Insert it into the cache, trimming the cache if necessary.
3310 */
3311 bool fInsertOkay = RTAvlU64Insert(&pVol->IdxNodeCacheRoot, &pNode->TreeNode);
3312 Assert(fInsertOkay);
3313 if (fInsertOkay)
3314 {
3315 pVol->cIdxNodes += 1;
3316 pVol->cbIdxNodes += pNode->cbCost;
3317 if (pVol->cbIdxNodes > RTFSNTFS_MAX_CORE_CACHE_SIZE)
3318 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
3319
3320 *ppNode = pNode;
3321 return VINF_SUCCESS;
3322 }
3323 }
3324 }
3325 }
3326 }
3327
3328 RTMemFree(pNode->pNode);
3329 pNode->pNode = NULL;
3330 }
3331 else
3332 rc = VERR_NO_MEMORY;
3333 RTMemFree(pNode);
3334 return rc;
3335}
3336
3337
3338/**
3339 * Frees resource kept by an index root info structure.
3340 *
3341 * @param pRootInfo The index root info structure to delete.
3342 */
3343static void rtFsNtfsIdxRootInfo_Delete(PRTFSNTFSIDXROOTINFO pRootInfo)
3344{
3345 rtFsNtfsIdxNodeInfo_Delete(&pRootInfo->NodeInfo);
3346 pRootInfo->pRootAttr->uObj.pSharedDir = NULL;
3347 rtFsNtfsCore_Release(pRootInfo->pRootAttr->pCore);
3348 pRootInfo->pRootAttr = NULL;
3349 pRootInfo->pAlloc = NULL;
3350 pRootInfo->pRoot = NULL;
3351}
3352
3353
3354/**
3355 * Destroys a shared directory structure when the reference count reached zero.
3356 *
3357 * @returns zero
3358 * @param pThis The shared directory structure to destroy.
3359 */
3360static uint32_t rtFsNtfsDirShrd_Destroy(PRTFSNTFSDIRSHRD pThis)
3361{
3362 rtFsNtfsIdxRootInfo_Delete(&pThis->RootInfo);
3363 RTMemFree(pThis);
3364 return 0;
3365}
3366
3367
3368/**
3369 * Releases a references to a shared directory structure.
3370 *
3371 * @returns New reference count.
3372 * @param pThis The shared directory structure.
3373 */
3374static uint32_t rtFsNtfsDirShrd_Release(PRTFSNTFSDIRSHRD pThis)
3375{
3376 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
3377 Assert(cRefs < 4096);
3378 if (cRefs > 0)
3379 return cRefs;
3380 return rtFsNtfsDirShrd_Destroy(pThis);
3381}
3382
3383
3384/**
3385 * Retains a references to a shared directory structure.
3386 *
3387 * @returns New reference count.
3388 * @param pThis The shared directory structure.
3389 */
3390static uint32_t rtFsNtfsDirShrd_Retain(PRTFSNTFSDIRSHRD pThis)
3391{
3392 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
3393 Assert(cRefs > 1);
3394 Assert(cRefs < 4096);
3395 return cRefs;
3396}
3397
3398
3399/**
3400 * Compares the two filenames in an case insentivie manner.
3401 *
3402 * @retval -1 if the first filename comes first
3403 * @retval 0 if equal
3404 * @retval 1 if the second filename comes first.
3405 *
3406 * @param pwszUpper1 The first filename, this has been uppercase already.
3407 * @param cwcUpper1 The length of the first filename.
3408 * @param pawcFilename2 The second filename to compare it with. Not zero
3409 * terminated.
3410 * @param cwcFilename2 The length of the second filename.
3411 * @param pawcUpcase The uppercase table. 64K entries.
3412 */
3413static int rtFsNtfsIdxComp_Filename(PCRTUTF16 pwszUpper1, uint8_t cwcUpper1, PCRTUTF16 pawcFilename2, uint8_t cwcFilename2,
3414 PCRTUTF16 const pawcUpcase)
3415{
3416 while (cwcUpper1 > 0 && cwcFilename2 > 0)
3417 {
3418 RTUTF16 uc1 = *pwszUpper1++;
3419 RTUTF16 uc2 = *pawcFilename2++;
3420 if (uc1 != uc2)
3421 {
3422 uc2 = pawcUpcase[uc2];
3423 if (uc1 != uc2)
3424 return uc1 < uc2 ? -1 : 1;
3425 }
3426
3427 /* Decrement the lengths and loop. */
3428 cwcUpper1--;
3429 cwcFilename2--;
3430 }
3431
3432 if (!cwcUpper1)
3433 {
3434 if (!cwcFilename2)
3435 return 0;
3436 return -1;
3437 }
3438 return 1;
3439}
3440
3441
3442/**
3443 * Look up a name in the directory.
3444 *
3445 * @returns IPRT status code.
3446 * @param pShared The shared directory structure.
3447 * @param pszEntry The name to lookup.
3448 * @param ppFilename Where to return the pointer to the filename structure.
3449 * @param ppEntryHdr Where to return the poitner to the entry header
3450 * structure.
3451 * @param ppNode Where to return the pointer to the node the filename
3452 * structure resides in. This must be released. It will
3453 * be set to NULL if the name was found in the root node.
3454 */
3455static int rtFsNtfsDirShrd_Lookup(PRTFSNTFSDIRSHRD pShared, const char *pszEntry,
3456 PCNTFSATFILENAME *ppFilename, PCNTFSIDXENTRYHDR *ppEntryHdr, PRTFSNTFSIDXNODE *ppNode)
3457{
3458 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
3459
3460 *ppFilename = NULL;
3461 *ppEntryHdr = NULL;
3462 *ppNode = NULL;
3463 /** @todo do streams (split on ':') */
3464
3465 /*
3466 * Convert the filename to UTF16 and uppercase.
3467 */
3468 PCRTUTF16 const pawcUpcase = pVol->pawcUpcase;
3469 RTUTF16 wszFilename[256+4];
3470 PRTUTF16 pwszDst = wszFilename;
3471 PRTUTF16 pwszEnd = &wszFilename[255];
3472 const char *pszSrc = pszEntry;
3473 for (;;)
3474 {
3475 RTUNICP uc;
3476 int rc = RTStrGetCpEx(&pszSrc, &uc);
3477 if (RT_SUCCESS(rc))
3478 {
3479 if (uc != 0)
3480 {
3481 if (uc < _64K)
3482 uc = pawcUpcase[uc];
3483 pwszDst = RTUtf16PutCp(pwszDst, uc);
3484 if ((uintptr_t)pwszDst <= (uintptr_t)pwszEnd)
3485 { /* likely */ }
3486 else
3487 {
3488 Log(("rtFsNtfsDirShrd_Lookup: Filename too long '%s'\n", pszEntry));
3489 return VERR_FILENAME_TOO_LONG;
3490 }
3491 }
3492 else
3493 {
3494 *pwszDst = '\0';
3495 break;
3496 }
3497 }
3498 else
3499 {
3500 Log(("rtFsNtfsDirShrd_Lookup: Invalid UTF-8 encoding (%Rrc): %.*Rhxs\n", rc, strlen(pszEntry), pszEntry));
3501 return rc;
3502 }
3503 }
3504 uint8_t const cwcFilename = (uint8_t)(pwszDst - wszFilename);
3505
3506 /*
3507 * Do the tree traversal.
3508 */
3509 PRTFSNTFSIDXROOTINFO pRootInfo = &pShared->RootInfo;
3510 PRTFSNTFSIDXNODEINFO pNodeInfo = &pRootInfo->NodeInfo;
3511 PRTFSNTFSIDXNODE pNode = NULL;
3512 for (;;)
3513 {
3514 /*
3515 * Search it.
3516 */
3517 PCNTFSIDXENTRYHDR *papEntries = pNodeInfo->papEntries;
3518 uint32_t iEnd = pNodeInfo->cEntries;
3519 AssertReturn(iEnd > 0, VERR_INTERNAL_ERROR_3);
3520
3521 /* Exclude the end node from the serach as it doesn't have any key. */
3522 if (papEntries[iEnd - 1]->fFlags & NTFSIDXENTRYHDR_F_END)
3523 iEnd--;
3524
3525 uint32_t iEntry;
3526 if (1 /*iEnd < 8*/ )
3527 {
3528 if (iEnd > 0)
3529 {
3530 for (iEntry = 0; iEntry < iEnd; iEntry++)
3531 {
3532 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(papEntries[iEntry] + 1);
3533 int iDiff = rtFsNtfsIdxComp_Filename(wszFilename, cwcFilename, pFilename->wszFilename,
3534 pFilename->cwcFilename, pawcUpcase);
3535 if (iDiff > 0)
3536 { /* likely */ }
3537 else if (iDiff == 0)
3538 {
3539 *ppNode = pNode;
3540 *ppEntryHdr = papEntries[iEntry];
3541 *ppFilename = pFilename;
3542 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Found it! (iEntry=%u, FileMftRec=%#RX64 sqn %#x)\n",
3543 pszEntry, iEntry, NTFSMFTREF_GET_IDX(&papEntries[iEntry]->u.FileMftRec),
3544 NTFSMFTREF_GET_SEQ(&papEntries[iEntry]->u.FileMftRec) ));
3545 return VINF_SUCCESS;
3546 }
3547 else
3548 break;
3549 }
3550 }
3551 else
3552 iEntry = iEnd;
3553 }
3554 /* else: implement binary search */
3555
3556 /*
3557 * Decend thru node iEntry.
3558 *
3559 * We could be bold and ASSUME that there is always an END node, but we're
3560 * playing safe for now.
3561 */
3562 if (iEnd < pNodeInfo->cEntries)
3563 {
3564 PCNTFSIDXENTRYHDR pEntry = papEntries[iEntry];
3565 if (pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
3566 {
3567 int64_t iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pEntry);
3568 rtFsNtfsIdxNode_Release(pNode);
3569 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
3570 if (RT_SUCCESS(rc))
3571 {
3572 pNodeInfo = &pNode->NodeInfo;
3573 continue;
3574 }
3575 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n",
3576 pszEntry, iSubnode, rc));
3577 return rc;
3578 }
3579 }
3580 rtFsNtfsIdxNode_Release(pNode);
3581 LogFlow(("rtFsNtfsDirShrd_Lookup(%s): Not found! (#2)\n", pszEntry));
3582 return VERR_FILE_NOT_FOUND;
3583 }
3584
3585 /* not reached */
3586}
3587
3588
3589/**
3590 * Gets the shared directory structure for the parent.
3591 *
3592 * @returns IPRT status code.
3593 * @param pThis The directory which parent we want.
3594 * @param ppDotDot Where to return the referenced shared parent dir
3595 * structure.
3596 *
3597 */
3598static int rtFsNtfsDirShrd_QueryParent(PRTFSNTFSDIRSHRD pThis, PRTFSNTFSDIRSHRD *ppDotDot)
3599{
3600 /*
3601 * The root directory has no parent from our perspective.
3602 */
3603 if (pThis == pThis->RootInfo.NodeInfo.pVol->pRootDir)
3604 {
3605 rtFsNtfsDirShrd_Retain(pThis);
3606 *ppDotDot = pThis;
3607 return VINF_SUCCESS;
3608 }
3609
3610 /*
3611 * Look for a filename record so we know where we go from here.
3612 */
3613 PRTFSNTFSCORE pCore = pThis->RootInfo.pRootAttr->pCore;
3614 PRTFSNTFSATTR pCurAttr;
3615 RTListForEach(&pCore->AttribHead, pCurAttr, RTFSNTFSATTR, ListEntry)
3616 {
3617 if ( pCurAttr->pAttrHdr->uAttrType == NTFS_AT_FILENAME
3618 && pCurAttr->cbResident >= RT_UOFFSETOF(NTFSATFILENAME, wszFilename))
3619 {
3620 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)NTFSATTRIBHDR_GET_RES_VALUE_PTR(pCurAttr->pAttrHdr);
3621 int rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pThis->RootInfo.NodeInfo.pVol, &pFilename->ParentDirMftRec,
3622 ppDotDot, NULL /*pErrInfo*/, "..");
3623 if (RT_SUCCESS(rc))
3624 return VINF_SUCCESS;
3625 LogRel(("rtFsNtfsDirShrd_QueryParent: rtFsNtfsVol_QueryOrCreateSharedDirByMftRef failed: %Rrc\n", rc));
3626 return rc;
3627 }
3628 }
3629
3630 LogRel(("rtFsNtfsDirShrd_QueryParent: Couldn't find '..' filename for MFT record %RX64!\n",
3631 pThis->RootInfo.pRootAttr->pCore->pMftRec->TreeNode.Key));
3632 return VERR_VFS_BOGUS_FORMAT;
3633}
3634
3635
3636
3637/**
3638 * Destroys an index node.
3639 *
3640 * This will remove it from the cache tree, however the caller must make sure
3641 * its not in the reuse list any more.
3642 *
3643 * @param pNode The node to destroy.
3644 */
3645static void rtFsNtfsIdxNode_Destroy(PRTFSNTFSIDXNODE pNode)
3646{
3647 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
3648
3649 /* Remove it from the volume node cache. */
3650 PAVLU64NODECORE pAssertRemove = RTAvlU64Remove(&pVol->IdxNodeCacheRoot, pNode->TreeNode.Key);
3651 Assert(pAssertRemove == &pNode->TreeNode); NOREF(pAssertRemove);
3652 pVol->cIdxNodes--;
3653 pVol->cbIdxNodes -= pNode->cbCost;
3654
3655 /* Destroy it. */
3656 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
3657 RTMemFree(pNode->pNode);
3658 pNode->pNode = NULL;
3659 RTMemFree(pNode);
3660}
3661
3662
3663/**
3664 * Trims the index node cache.
3665 *
3666 * @param pThis The NTFS volume instance which index node cache
3667 * needs trimming.
3668 */
3669static void rtFsNtfsIdxVol_TrimIndexNodeCache(PRTFSNTFSVOL pThis)
3670{
3671 while ( pThis->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE
3672 && pThis->cUnusedIdxNodes)
3673 {
3674 PRTFSNTFSIDXNODE pNode = RTListRemoveFirst(&pThis->IdxNodeUnusedHead, RTFSNTFSIDXNODE, UnusedListEntry);
3675 pThis->cUnusedIdxNodes--;
3676 rtFsNtfsIdxNode_Destroy(pNode);
3677 }
3678}
3679
3680
3681/**
3682 * Index node reference reached zero, put it in the unused list and trim the
3683 * cache.
3684 *
3685 * @returns zero
3686 * @param pNode The index node.
3687 */
3688static uint32_t rtFsNtfsIdxNode_MaybeDestroy(PRTFSNTFSIDXNODE pNode)
3689{
3690 PRTFSNTFSVOL pVol = pNode->NodeInfo.pVol;
3691 if (pVol)
3692 {
3693 RTListAppend(&pVol->IdxNodeUnusedHead, &pNode->UnusedListEntry);
3694 pVol->cUnusedIdxNodes++;
3695 if (pVol->cbIdxNodes > RTFSNTFS_MAX_NODE_CACHE_SIZE)
3696 rtFsNtfsIdxVol_TrimIndexNodeCache(pVol);
3697 return 0;
3698 }
3699 /* not sure if this is needed yet... */
3700 rtFsNtfsIdxNodeInfo_Delete(&pNode->NodeInfo);
3701 RTMemFree(pNode);
3702 return 0;
3703}
3704
3705
3706/**
3707 * Releases a reference to an index node.
3708 *
3709 * @returns New reference count.
3710 * @param pNode The index node to release. NULL is ignored.
3711 */
3712static uint32_t rtFsNtfsIdxNode_Release(PRTFSNTFSIDXNODE pNode)
3713{
3714 if (pNode)
3715 {
3716 uint32_t cRefs = ASMAtomicDecU32(&pNode->cRefs);
3717 Assert(cRefs < 128);
3718 if (cRefs > 0)
3719 return cRefs;
3720 return rtFsNtfsIdxNode_MaybeDestroy(pNode);
3721 }
3722 return 0;
3723}
3724
3725
3726/**
3727 * Retains a reference to an index node.
3728 *
3729 * This will remove it from the unused list if necessary.
3730 *
3731 * @returns New reference count.
3732 * @param pNode The index to reference.
3733 */
3734static uint32_t rtFsNtfsIdxNode_Retain(PRTFSNTFSIDXNODE pNode)
3735{
3736 uint32_t cRefs = ASMAtomicIncU32(&pNode->cRefs);
3737 if (cRefs == 1)
3738 {
3739 RTListNodeRemove(&pNode->UnusedListEntry);
3740 pNode->NodeInfo.pVol->cUnusedIdxNodes--;
3741 }
3742 return cRefs;
3743}
3744
3745
3746
3747
3748/*
3749 *
3750 * Directory instance methods
3751 * Directory instance methods
3752 * Directory instance methods
3753 *
3754 */
3755
3756/**
3757 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
3758 */
3759static DECLCALLBACK(int) rtFsNtfsDir_Close(void *pvThis)
3760{
3761 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3762 LogFlow(("rtFsNtfsDir_Close(%p/%p)\n", pThis, pThis->pShared));
3763
3764 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3765 pThis->pShared = NULL;
3766 if (pShared)
3767 rtFsNtfsDirShrd_Release(pShared);
3768
3769 while (pThis->cEnumStackEntries > 0)
3770 {
3771 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
3772 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
3773 pEntry->pNodeInfo = NULL;
3774 }
3775 RTMemFree(pThis->paEnumStack);
3776 pThis->paEnumStack = NULL;
3777 pThis->cEnumStackMaxDepth = 0;
3778
3779 return VINF_SUCCESS;
3780}
3781
3782
3783/**
3784 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
3785 */
3786static DECLCALLBACK(int) rtFsNtfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
3787{
3788 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3789 Log(("rtFsNtfsDir_QueryInfo\n"));
3790 return rtFsNtfsCore_QueryInfo(pThis->pShared->RootInfo.pRootAttr->pCore,
3791 pThis->pShared->RootInfo.pAlloc ? pThis->pShared->RootInfo.pAlloc
3792 : pThis->pShared->RootInfo.pRootAttr,
3793 pObjInfo, enmAddAttr);
3794}
3795
3796
3797/**
3798 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
3799 */
3800static DECLCALLBACK(int) rtFsNtfsDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
3801{
3802 Log(("rtFsNtfsDir_SetMode\n"));
3803 RT_NOREF(pvThis, fMode, fMask);
3804 return VERR_WRITE_PROTECT;
3805}
3806
3807
3808/**
3809 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
3810 */
3811static DECLCALLBACK(int) rtFsNtfsDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
3812 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
3813{
3814 Log(("rtFsNtfsDir_SetTimes\n"));
3815 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
3816 return VERR_WRITE_PROTECT;
3817}
3818
3819
3820/**
3821 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
3822 */
3823static DECLCALLBACK(int) rtFsNtfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
3824{
3825 Log(("rtFsNtfsDir_SetOwner\n"));
3826 RT_NOREF(pvThis, uid, gid);
3827 return VERR_WRITE_PROTECT;
3828}
3829
3830
3831/**
3832 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
3833 */
3834static DECLCALLBACK(int) rtFsNtfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
3835 uint32_t fFlags, PRTVFSOBJ phVfsObj)
3836{
3837 LogFlow(("rtFsNtfsDir_Open: pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
3838 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
3839 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
3840 PRTFSNTFSVOL pVol = pShared->RootInfo.NodeInfo.pVol;
3841 int rc;
3842
3843 /*
3844 * We cannot create or replace anything, just open stuff.
3845 */
3846 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
3847 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
3848 { /* likely */ }
3849 else
3850 return VERR_WRITE_PROTECT;
3851
3852 /*
3853 * Special cases '.' and '..'
3854 */
3855 if ( pszEntry[0] == '.'
3856 && ( pszEntry[1] == '\0'
3857 || ( pszEntry[1] == '.'
3858 && pszEntry[2] == '\0')))
3859 {
3860 if (!(fFlags & RTVFSOBJ_F_OPEN_DIRECTORY))
3861 return VERR_IS_A_DIRECTORY;
3862
3863 PRTFSNTFSDIRSHRD pSharedToOpen;
3864 if (pszEntry[1] == '\0')
3865 {
3866 pSharedToOpen = pShared;
3867 rtFsNtfsDirShrd_Retain(pSharedToOpen);
3868 rc = VINF_SUCCESS;
3869 }
3870 else
3871 {
3872 pSharedToOpen = NULL;
3873 rc = rtFsNtfsDirShrd_QueryParent(pShared, &pSharedToOpen);
3874 }
3875 if (RT_SUCCESS(rc))
3876 {
3877 RTVFSDIR hVfsDir;
3878 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3879 rtFsNtfsDirShrd_Release(pSharedToOpen);
3880 if (RT_SUCCESS(rc))
3881 {
3882 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3883 RTVfsDirRelease(hVfsDir);
3884 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3885 }
3886 }
3887 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3888 return rc;
3889 }
3890
3891 /*
3892 * Lookup the index entry.
3893 */
3894 PRTFSNTFSIDXNODE pNode;
3895 PCNTFSIDXENTRYHDR pEntryHdr;
3896 PCNTFSATFILENAME pFilename;
3897 rc = rtFsNtfsDirShrd_Lookup(pShared, pszEntry, &pFilename, &pEntryHdr, &pNode);
3898 if (RT_SUCCESS(rc))
3899 {
3900 uint32_t fFileAttribs = RT_LE2H_U32(pFilename->fFileAttribs);
3901 switch (fFileAttribs & (NTFS_FA_DIRECTORY | NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT))
3902 {
3903 /*
3904 * File.
3905 */
3906 case 0:
3907 if (fFlags & RTVFSOBJ_F_OPEN_FILE)
3908 {
3909 RTVFSFILE hVfsFile;
3910 rc = rtFsNtfsVol_NewFile(pVol, fOpen, pEntryHdr, NULL /*pszStreamName*/, &hVfsFile, NULL, pszEntry);
3911 if (RT_SUCCESS(rc))
3912 {
3913 *phVfsObj = RTVfsObjFromFile(hVfsFile);
3914 RTVfsFileRelease(hVfsFile);
3915 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3916 }
3917 }
3918 else
3919 rc = VERR_IS_A_FILE;
3920 break;
3921
3922 /*
3923 * Directory
3924 */
3925 case NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3926 case NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3927 case NTFS_FA_DIRECTORY:
3928 if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
3929 {
3930 PRTFSNTFSDIRSHRD pSharedToOpen;
3931 rc = rtFsNtfsVol_QueryOrCreateSharedDirByMftRef(pVol, &pEntryHdr->u.FileMftRec,
3932 &pSharedToOpen, NULL, pszEntry);
3933 if (RT_SUCCESS(rc))
3934 {
3935 RTVFSDIR hVfsDir;
3936 rc = rtFsNtfsVol_NewDirFromShared(pVol, pSharedToOpen, &hVfsDir);
3937 rtFsNtfsDirShrd_Release(pSharedToOpen);
3938 if (RT_SUCCESS(rc))
3939 {
3940 *phVfsObj = RTVfsObjFromDir(hVfsDir);
3941 RTVfsDirRelease(hVfsDir);
3942 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
3943 }
3944 }
3945 }
3946 else
3947 rc = VERR_IS_A_DIRECTORY;
3948 break;
3949
3950 /*
3951 * Possible symbolic links.
3952 */
3953 case NTFS_FA_REPARSE_POINT:
3954 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY:
3955 case NTFS_FA_REPARSE_POINT | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3956 case NTFS_FA_REPARSE_POINT | NTFS_FA_DIRECTORY | NTFS_FA_DUP_FILE_NAME_INDEX_PRESENT:
3957 rc = VERR_NOT_IMPLEMENTED;
3958 break;
3959
3960 default:
3961 AssertFailed();
3962 rc = VERR_FILE_NOT_FOUND;
3963 break;
3964 }
3965 rtFsNtfsIdxNode_Release(pNode);
3966 }
3967
3968 LogFlow(("rtFsNtfsDir_Open(%s): returns %Rrc\n", pszEntry, rc));
3969 return rc;
3970}
3971
3972
3973/**
3974 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
3975 */
3976static DECLCALLBACK(int) rtFsNtfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
3977{
3978 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
3979 Log(("rtFsNtfsDir_CreateDir\n"));
3980 return VERR_WRITE_PROTECT;
3981}
3982
3983
3984/**
3985 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
3986 */
3987static DECLCALLBACK(int) rtFsNtfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
3988{
3989 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
3990 Log(("rtFsNtfsDir_OpenSymlink\n"));
3991 return VERR_NOT_SUPPORTED;
3992}
3993
3994
3995/**
3996 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
3997 */
3998static DECLCALLBACK(int) rtFsNtfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
3999 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
4000{
4001 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
4002 Log(("rtFsNtfsDir_CreateSymlink\n"));
4003 return VERR_WRITE_PROTECT;
4004}
4005
4006
4007/**
4008 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
4009 */
4010static DECLCALLBACK(int) rtFsNtfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
4011{
4012 RT_NOREF(pvThis, pszEntry, fType);
4013 Log(("rtFsNtfsDir_UnlinkEntry\n"));
4014 return VERR_WRITE_PROTECT;
4015}
4016
4017
4018/**
4019 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
4020 */
4021static DECLCALLBACK(int) rtFsNtfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
4022{
4023 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
4024 Log(("rtFsNtfsDir_RenameEntry\n"));
4025 return VERR_WRITE_PROTECT;
4026}
4027
4028
4029/**
4030 * Cleans up the directory enumeration stack, releasing all node references.
4031 *
4032 * @param pThis The open directory instance data.
4033 */
4034static void rtFsNtfsDir_StackCleanup(PRTFSNTFSDIR pThis)
4035{
4036 while (pThis->cEnumStackEntries > 0)
4037 {
4038 PRTFSNTFSIDXSTACKENTRY pEntry = &pThis->paEnumStack[--pThis->cEnumStackEntries];
4039 rtFsNtfsIdxNode_Release(pEntry->pNodeInfo->pNode);
4040 pEntry->pNodeInfo = NULL;
4041 }
4042 if (pThis->paEnumStack)
4043 pThis->paEnumStack[0].iNext = 0;
4044}
4045
4046
4047/**
4048 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
4049 */
4050static DECLCALLBACK(int) rtFsNtfsDir_RewindDir(void *pvThis)
4051{
4052 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
4053 LogFlow(("rtFsNtfsDir_RewindDir\n"));
4054
4055 rtFsNtfsDir_StackCleanup(pThis);
4056 pThis->fNoMoreFiles = false;
4057
4058 return VINF_SUCCESS;
4059}
4060
4061/**
4062 * Descends down @a iSubnode to the first entry in left most leaf node.
4063 *
4064 * @returns IPRT status code.
4065 * @param pThis The open directory instance data.
4066 * @param pRootInfo The root info structure.
4067 * @param iSubnode The subnode address to descend thru.
4068 */
4069static int rtFsNtfsDir_StackDescend(PRTFSNTFSDIR pThis, PRTFSNTFSIDXROOTINFO pRootInfo, int64_t iSubnode)
4070{
4071 for (;;)
4072 {
4073 /* Load the node. */
4074 PRTFSNTFSIDXNODE pNode;
4075 int rc = rtFsNtfsIdxRootInfo_QueryNode(pRootInfo, iSubnode, &pNode);
4076 if (RT_SUCCESS(rc))
4077 { /* likely */ }
4078 else
4079 {
4080 LogFlow(("rtFsNtfsDir_StackDescend: rtFsNtfsIdxRootInfo_QueryNode(%#RX64) error %Rrc!\n", iSubnode, rc));
4081 return rc;
4082 }
4083
4084 /* Push it onto the stack. */
4085 uint32_t iStack = pThis->cEnumStackEntries;
4086 if (iStack + 1 < pThis->cEnumStackMaxDepth)
4087 { /* likely */ }
4088 else if (pThis->cEnumStackMaxDepth < 1024)
4089 {
4090 Assert(pThis->cEnumStackMaxDepth> 0);
4091 uint32_t cDepth = pThis->cEnumStackMaxDepth * 2;
4092 Log5(("rtFsNtfsDir_ReadDir: Growing stack size to %u entries (from %u)\n", cDepth, pThis->cEnumStackMaxDepth));
4093 void *pvNew = RTMemRealloc(pThis->paEnumStack, cDepth * sizeof(pThis->paEnumStack[0]));
4094 if (pvNew)
4095 pThis->paEnumStack = (PRTFSNTFSIDXSTACKENTRY)pvNew;
4096 else
4097 return VERR_NO_MEMORY;
4098 pThis->cEnumStackMaxDepth = cDepth;
4099 }
4100 else
4101 {
4102 LogRel(("rtFsNtfsDir_StackDescend: Badly unbalanced index! (MFT record #%#RX64) -> VERR_VFS_BOGUS_FORMAT\n",
4103 pThis->pShared->RootInfo.pRootAttr->pCore->pMftRec->TreeNode.Key));
4104 return VERR_VFS_BOGUS_FORMAT;
4105 }
4106
4107 Log5(("rtFsNtfsDir_ReadDir: pushing %#RX64 (cEntries=%u, iStack=%u)\n", iSubnode, pNode->NodeInfo.cEntries, iStack));
4108 pThis->paEnumStack[iStack].iNext = 0;
4109 pThis->paEnumStack[iStack].fDescend = false;
4110 pThis->paEnumStack[iStack].pNodeInfo = &pNode->NodeInfo;
4111 pThis->cEnumStackEntries = iStack + 1;
4112
4113 /* Stop if this is a leaf node. */
4114 if ( !pNode->NodeInfo.fInternal
4115 || !pNode->NodeInfo.cEntries /* paranoia */)
4116 return VINF_SUCCESS;
4117
4118 /* Get the first entry and check that it's an internal node before trying to following it. */
4119 PCNTFSIDXENTRYHDR pFirstEntry = pNode->NodeInfo.papEntries[0];
4120 if (pFirstEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
4121 { /* likely */ }
4122 else
4123 return VINF_SUCCESS;
4124 iSubnode = NTFSIDXENTRYHDR_GET_SUBNODE(pFirstEntry);
4125 }
4126}
4127
4128
4129/**
4130 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
4131 */
4132static DECLCALLBACK(int) rtFsNtfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
4133 RTFSOBJATTRADD enmAddAttr)
4134{
4135 PRTFSNTFSDIR pThis = (PRTFSNTFSDIR)pvThis;
4136 PRTFSNTFSDIRSHRD pShared = pThis->pShared;
4137 int rc;
4138 Log(("rtFsNtfsDir_ReadDir\n"));
4139
4140 /*
4141 * Return immediately if no files at hand.
4142 */
4143 if (pThis->fNoMoreFiles)
4144 return VERR_NO_MORE_FILES;
4145
4146 /*
4147 * Make sure we've got a stack before we jump into the fray.
4148 */
4149 if (!pThis->cEnumStackMaxDepth)
4150 {
4151 uint32_t cDepth;
4152 if (!pShared->RootInfo.pAlloc)
4153 cDepth = 2;
4154 else
4155 {
4156 cDepth = ASMBitFirstSetU64(pShared->RootInfo.pAlloc->cbValue / RT_LE2H_U32(pShared->RootInfo.pRoot->cbIndexNode));
4157 cDepth += 3;
4158 }
4159
4160 pThis->paEnumStack = (PRTFSNTFSIDXSTACKENTRY)RTMemAllocZ(cDepth * sizeof(pThis->paEnumStack[0]));
4161 if (!pThis->paEnumStack)
4162 return VERR_NO_MEMORY;
4163 pThis->cEnumStackMaxDepth = cDepth;
4164 pThis->cEnumStackEntries = 0;
4165 Log5(("rtFsNtfsDir_ReadDir: Initial stack size: %u entries\n", cDepth));
4166 //pThis->paEnumStack[0].iNext = 0;
4167 }
4168
4169 /*
4170 * Deal with '.' and '..' by using stack entry zero without setting cEnumStack to zero.
4171 * This is fine because we've got the fNoMoreFiles flag that got checked already.
4172 */
4173 size_t const cbDirEntry = *pcbDirEntry;
4174 if (pThis->cEnumStackEntries == 0)
4175 {
4176 if (pThis->paEnumStack[0].iNext <= 1)
4177 {
4178
4179 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[pThis->paEnumStack[0].iNext + 2]);
4180 if (*pcbDirEntry > cbDirEntry)
4181 return VERR_BUFFER_OVERFLOW;
4182
4183 /* Names. */
4184 pDirEntry->cbName = pThis->paEnumStack[0].iNext + 1;
4185 pDirEntry->szName[0] = '.';
4186 pDirEntry->szName[pDirEntry->cbName - 1] = '.';
4187 pDirEntry->szName[pDirEntry->cbName] = '\0';
4188 pDirEntry->wszShortName[0] = '\0';
4189 pDirEntry->cwcShortName = 0;
4190
4191 /* Get referenced shared directory structure that we return info about. */
4192 PRTFSNTFSDIRSHRD pDotShared;
4193 if (pThis->paEnumStack[0].iNext == 0)
4194 {
4195 rtFsNtfsDirShrd_Retain(pShared);
4196 pDotShared = pShared;
4197 }
4198 else
4199 {
4200 pDotShared = NULL;
4201 rc = rtFsNtfsDirShrd_QueryParent(pShared, &pDotShared);
4202 if (RT_FAILURE(rc))
4203 {
4204 LogRel(("rtFsNtfsDir_ReadDir: couldn't find '..' filename! %Rrc\n", rc));
4205 return rc;
4206 }
4207 }
4208
4209 /* Get the info. */
4210 rc = rtFsNtfsCore_QueryInfo(pDotShared->RootInfo.pRootAttr->pCore, pDotShared->RootInfo.pRootAttr,
4211 &pDirEntry->Info, enmAddAttr);
4212 rtFsNtfsDirShrd_Release(pDotShared);
4213 if (RT_SUCCESS(rc))
4214 pThis->paEnumStack[0].iNext++;
4215 Log5(("rtFsNtfsDir_ReadDir: => '%s' (%Rrc)\n", pDirEntry->szName, rc));
4216 return rc;
4217 }
4218
4219 /*
4220 * Push the root onto the stack and decend down the left side of the tree.
4221 */
4222 PRTFSNTFSIDXNODEINFO pNodeInfo = &pShared->RootInfo.NodeInfo;
4223 pThis->paEnumStack[0].pNodeInfo = pNodeInfo;
4224 pThis->paEnumStack[0].iNext = 0;
4225 pThis->cEnumStackEntries = 1;
4226 Log5(("rtFsNtfsDir_ReadDir: pushing root\n"));
4227 if ( pNodeInfo->fInternal
4228 && pNodeInfo->cEntries > 0
4229 && (pNodeInfo->papEntries[0]->fFlags & NTFSIDXENTRYHDR_F_INTERNAL) /* parnaoia */ )
4230 {
4231 rc = rtFsNtfsDir_StackDescend(pThis, &pShared->RootInfo, NTFSIDXENTRYHDR_GET_SUBNODE(pNodeInfo->papEntries[0]));
4232 if (RT_FAILURE(rc))
4233 {
4234 pThis->fNoMoreFiles = true;
4235 rtFsNtfsDir_StackCleanup(pThis);
4236 return rc;
4237 }
4238 }
4239 }
4240
4241 /*
4242 * Work the stack.
4243 */
4244 int32_t iStack = pThis->cEnumStackEntries - 1;
4245 while (iStack >= 0)
4246 {
4247 PRTFSNTFSIDXNODEINFO pNodeInfo = pThis->paEnumStack[iStack].pNodeInfo;
4248 uint32_t iNext = pThis->paEnumStack[iStack].iNext;
4249 if (iNext < pNodeInfo->cEntries)
4250 {
4251 PCNTFSIDXENTRYHDR pEntry = pNodeInfo->papEntries[iNext];
4252 if ( !(pEntry->fFlags & NTFSIDXENTRYHDR_F_INTERNAL)
4253 || !pThis->paEnumStack[iStack].fDescend)
4254 {
4255 if (!(pEntry->fFlags & NTFSIDXENTRYHDR_F_END))
4256 {
4257 /*
4258 * Try return the current entry.
4259 */
4260 PCNTFSATFILENAME pFilename = (PCNTFSATFILENAME)(pEntry + 1);
4261
4262 /* Deal with the filename. */
4263 size_t cchFilename;
4264 rc = RTUtf16CalcUtf8LenEx(pFilename->wszFilename, pFilename->cwcFilename, &cchFilename);
4265 if (RT_FAILURE(rc))
4266 {
4267 cchFilename = 48;
4268 LogRel(("rtFsNtfsDir_ReadDir: Bad filename (%Rrc) %.*Rhxs\n",
4269 rc, pFilename->cwcFilename * sizeof(RTUTF16), pFilename->wszFilename));
4270 }
4271 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchFilename + 1]);
4272 if (*pcbDirEntry > cbDirEntry)
4273 {
4274 Log5(("rtFsNtfsDir_ReadDir: returns VERR_BUFFER_OVERFLOW (for '%.*ls')\n",
4275 pFilename->cwcFilename, pFilename->wszFilename));
4276 return VERR_BUFFER_OVERFLOW;
4277 }
4278
4279 char *pszDst = pDirEntry->szName;
4280 if (RT_SUCCESS(rc))
4281 rc = RTUtf16ToUtf8Ex(pFilename->wszFilename, pFilename->cwcFilename, &pszDst,
4282 cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName), &cchFilename);
4283 if (RT_FAILURE(rc))
4284 cchFilename = RTStrPrintf(pDirEntry->szName, cbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName),
4285 "{invalid-name-%#RX64}", NTFSMFTREF_GET_IDX(&pEntry->u.FileMftRec));
4286 pDirEntry->cbName = (uint16_t)cchFilename;
4287
4288 /* Figure out how to detect short names. */
4289 pDirEntry->cwcShortName = 0;
4290 pDirEntry->wszShortName[0] = '\0';
4291
4292 /* Standard attributes: file mode, sizes and timestamps. */
4293 pDirEntry->Info.cbObject = RT_LE2H_U64(pFilename->cbData);
4294 pDirEntry->Info.cbAllocated = RT_LE2H_U64(pFilename->cbAllocated);
4295 RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, RT_LE2H_U64(pFilename->iCreationTime));
4296 RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, RT_LE2H_U64(pFilename->iLastDataModTime));
4297 RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, RT_LE2H_U64(pFilename->iLastMftModTime));
4298 RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, RT_LE2H_U64(pFilename->iLastAccessTime));
4299 pDirEntry->Info.Attr.fMode = rtFsNtfsConvertFileattribsToMode(RT_LE2H_U32(pFilename->fFileAttribs), pFilename,
4300 RT_LE2H_U16(pEntry->cbKey));
4301
4302 /* additional stuff. */
4303 switch (enmAddAttr)
4304 {
4305 case RTFSOBJATTRADD_NOTHING:
4306 enmAddAttr = RTFSOBJATTRADD_UNIX;
4307 RT_FALL_THRU();
4308 case RTFSOBJATTRADD_UNIX:
4309 pDirEntry->Info.Attr.u.Unix.uid = NIL_RTUID;
4310 pDirEntry->Info.Attr.u.Unix.gid = NIL_RTGID;
4311 pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
4312 pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0;
4313 pDirEntry->Info.Attr.u.Unix.INodeId = NTFSMFTREF_GET_IDX(&pEntry->u.FileMftRec);
4314 pDirEntry->Info.Attr.u.Unix.fFlags = 0;
4315 pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
4316 pDirEntry->Info.Attr.u.Unix.Device = 0;
4317 break;
4318
4319 case RTFSOBJATTRADD_UNIX_OWNER:
4320 pDirEntry->Info.Attr.u.UnixOwner.uid = NIL_RTUID;
4321 pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0';
4322 break;
4323
4324 case RTFSOBJATTRADD_UNIX_GROUP:
4325 pDirEntry->Info.Attr.u.UnixGroup.gid = NIL_RTGID;
4326 pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
4327 break;
4328
4329 case RTFSOBJATTRADD_EASIZE:
4330 if (!(pFilename->fFileAttribs & RT_H2LE_U32_C(NTFS_FA_REPARSE_POINT)))
4331 pDirEntry->Info.Attr.u.EASize.cb = pFilename->u.cbPackedEas;
4332 else
4333 pDirEntry->Info.Attr.u.EASize.cb = 0;
4334 break;
4335
4336 default:
4337 AssertFailed();
4338 RT_ZERO(pDirEntry->Info.Attr.u);
4339 break;
4340 }
4341 pDirEntry->Info.Attr.enmAdditional = enmAddAttr;
4342
4343 /*
4344 * Advance the stack entry to the next entry and return.
4345 */
4346 Log5(("rtFsNtfsDir_ReadDir: => iStack=%u iNext=%u - '%.*ls'\n",
4347 iStack, iNext, pFilename->cwcFilename, pFilename->wszFilename));
4348 pThis->paEnumStack[iStack].iNext = iNext + 1;
4349 pThis->paEnumStack[iStack].fDescend = true;
4350 return VINF_SUCCESS;
4351 }
4352
4353 /*
4354 * End node, so pop it. We join the beoynd-end-of-entries path
4355 * further down, forcing the descend code to use continue.
4356 */
4357 }
4358 else
4359 {
4360 /*
4361 * Descend.
4362 */
4363 rc = rtFsNtfsDir_StackDescend(pThis, &pShared->RootInfo,
4364 NTFSIDXENTRYHDR_GET_SUBNODE(pNodeInfo->papEntries[iNext]));
4365 if (RT_SUCCESS(rc))
4366 {
4367 pThis->paEnumStack[iStack].fDescend = false;
4368 iStack = pThis->cEnumStackEntries - 1;
4369 continue;
4370 }
4371 pThis->fNoMoreFiles = true;
4372 rtFsNtfsDir_StackCleanup(pThis);
4373 return rc;
4374 }
4375 }
4376
4377 /*
4378 * Pop at stack entry.
4379 */
4380 Log5(("rtFsNtfsDir_ReadDir: popping %#RX64 (iNext=%u, cEntries=%u, iStack=%u) -> %#RX64 (iNext=%d, cEntries=%u)\n",
4381 pNodeInfo->pNode ? pNodeInfo->pNode->pNode->iSelfAddress : 0, iNext, pNodeInfo->cEntries, iStack,
4382 iStack > 0 && pThis->paEnumStack[iStack - 1].pNodeInfo->pNode
4383 ? pThis->paEnumStack[iStack - 1].pNodeInfo->pNode->pNode->iSelfAddress : UINT64_MAX,
4384 iStack > 0 ? pThis->paEnumStack[iStack - 1].iNext : -1,
4385 iStack > 0 ? pThis->paEnumStack[iStack - 1].pNodeInfo->cEntries : 0 ));
4386 rtFsNtfsIdxNode_Release(pNodeInfo->pNode);
4387 pThis->paEnumStack[iStack].pNodeInfo = NULL;
4388 pThis->cEnumStackEntries = iStack;
4389 iStack--;
4390 Assert(iStack < 0 || !pThis->paEnumStack[iStack].fDescend);
4391 }
4392
4393 /*
4394 * The End.
4395 */
4396 Log5(("rtFsNtfsDir_ReadDir: no more files\n"));
4397 pThis->fNoMoreFiles = true;
4398 return VERR_NO_MORE_FILES;
4399}
4400
4401
4402/**
4403 * NTFS directory operations.
4404 */
4405static const RTVFSDIROPS g_rtFsNtfsDirOps =
4406{
4407 { /* Obj */
4408 RTVFSOBJOPS_VERSION,
4409 RTVFSOBJTYPE_DIR,
4410 "NTFS Dir",
4411 rtFsNtfsDir_Close,
4412 rtFsNtfsDir_QueryInfo,
4413 NULL,
4414 RTVFSOBJOPS_VERSION
4415 },
4416 RTVFSDIROPS_VERSION,
4417 0,
4418 { /* ObjSet */
4419 RTVFSOBJSETOPS_VERSION,
4420 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
4421 rtFsNtfsDir_SetMode,
4422 rtFsNtfsDir_SetTimes,
4423 rtFsNtfsDir_SetOwner,
4424 RTVFSOBJSETOPS_VERSION
4425 },
4426 rtFsNtfsDir_Open,
4427 NULL /* pfnFollowAbsoluteSymlink */,
4428 NULL /* pfnOpenFile */,
4429 NULL /* pfnOpenDir */,
4430 rtFsNtfsDir_CreateDir,
4431 rtFsNtfsDir_OpenSymlink,
4432 rtFsNtfsDir_CreateSymlink,
4433 NULL /* pfnQueryEntryInfo */,
4434 rtFsNtfsDir_UnlinkEntry,
4435 rtFsNtfsDir_RenameEntry,
4436 rtFsNtfsDir_RewindDir,
4437 rtFsNtfsDir_ReadDir,
4438 RTVFSDIROPS_VERSION,
4439};
4440
4441
4442/**
4443 * Creates a new directory instance given a shared directory structure.
4444 *
4445 * @returns IPRT status code.
4446 * @param pThis The NTFS volume instance.
4447 * @param pSharedDir The shared directory structure to create a new
4448 * handle to.
4449 * @param phVfsDir Where to return the directory handle.
4450 */
4451static int rtFsNtfsVol_NewDirFromShared(PRTFSNTFSVOL pThis, PRTFSNTFSDIRSHRD pSharedDir, PRTVFSDIR phVfsDir)
4452{
4453 PRTFSNTFSDIR pNewDir;
4454 int rc = RTVfsNewDir(&g_rtFsNtfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
4455 phVfsDir, (void **)&pNewDir);
4456 if (RT_SUCCESS(rc))
4457 {
4458 rtFsNtfsDirShrd_Retain(pSharedDir);
4459 pNewDir->pShared = pSharedDir;
4460 pNewDir->cEnumStackEntries = 0;
4461 pNewDir->cEnumStackMaxDepth = 0;
4462 pNewDir->paEnumStack = NULL;
4463 return VINF_SUCCESS;
4464 }
4465 return rc;
4466}
4467
4468
4469
4470/*
4471 *
4472 * Volume level code.
4473 * Volume level code.
4474 * Volume level code.
4475 *
4476 */
4477
4478
4479/**
4480 * Slow path for querying the allocation state of a cluster.
4481 *
4482 * @returns IPRT status code.
4483 * @param pThis The NTFS volume instance.
4484 * @param iCluster The cluster to query.
4485 * @param pfState Where to return the state.
4486 */
4487static int rtFsNtfsVol_QueryClusterStateSlow(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
4488{
4489 int rc;
4490 uint64_t const cbWholeBitmap = RT_LE2H_U64(pThis->pMftBitmap->pAttrHdr->u.NonRes.cbData);
4491 uint64_t const offInBitmap = iCluster >> 3;
4492 if (offInBitmap < cbWholeBitmap)
4493 {
4494 if (!pThis->pvBitmap)
4495 {
4496 /*
4497 * Try cache the whole bitmap if it's not too large.
4498 */
4499 if ( cbWholeBitmap <= RTFSNTFS_MAX_WHOLE_BITMAP_CACHE
4500 && cbWholeBitmap >= RT_ALIGN_64(pThis->cClusters >> 3, 8))
4501 {
4502 pThis->cbBitmapAlloc = RT_ALIGN_Z((uint32_t)cbWholeBitmap, 8);
4503 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
4504 if (pThis->pvBitmap)
4505 {
4506 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
4507 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, 0, pThis->pvBitmap, (uint32_t)cbWholeBitmap);
4508 if (RT_SUCCESS(rc))
4509 {
4510 pThis->iFirstBitmapCluster = 0;
4511 pThis->cBitmapClusters = pThis->cClusters;
4512 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iCluster);
4513 return VINF_SUCCESS;
4514 }
4515 RTMemFree(pThis->pvBitmap);
4516 pThis->pvBitmap = NULL;
4517 pThis->cbBitmapAlloc = 0;
4518 return rc;
4519 }
4520 }
4521
4522 /*
4523 * Do a cluster/4K cache.
4524 */
4525 pThis->cbBitmapAlloc = RT_MAX(pThis->cbCluster, _4K);
4526 pThis->pvBitmap = RTMemAlloc(pThis->cbBitmapAlloc);
4527 if (!pThis->pvBitmap)
4528 {
4529 pThis->cbBitmapAlloc = 0;
4530 return VERR_NO_MEMORY;
4531 }
4532 }
4533
4534 /*
4535 * Load a cache line.
4536 */
4537 Assert(RT_IS_POWER_OF_TWO(pThis->cbBitmapAlloc));
4538 uint64_t offLoad = offInBitmap & ~(uint64_t)(pThis->cbBitmapAlloc - 1);
4539 uint32_t cbLoad = (uint32_t)RT_MIN(cbWholeBitmap - offLoad, pThis->cbBitmapAlloc);
4540
4541 memset(pThis->pvBitmap, 0xff, pThis->cbBitmapAlloc);
4542 rc = rtFsNtfsAttr_Read(pThis->pMftBitmap, offLoad, pThis->pvBitmap, cbLoad);
4543 if (RT_SUCCESS(rc))
4544 {
4545 pThis->iFirstBitmapCluster = offLoad << 3;
4546 pThis->cBitmapClusters = cbLoad << 3;
4547 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)(iCluster - pThis->iFirstBitmapCluster));
4548 return VINF_SUCCESS;
4549 }
4550 pThis->cBitmapClusters = 0;
4551 }
4552 else
4553 {
4554 LogRel(("rtFsNtfsVol_QueryClusterStateSlow: iCluster=%#RX64 is outside the bitmap (%#RX64)\n", iCluster, cbWholeBitmap));
4555 rc = VERR_OUT_OF_RANGE;
4556 }
4557 return rc;
4558}
4559
4560
4561/**
4562 * Query the allocation state of the given cluster.
4563 *
4564 * @returns IPRT status code.
4565 * @param pThis The NTFS volume instance.
4566 * @param iCluster The cluster to query.
4567 * @param pfState Where to return the state.
4568 */
4569static int rtFsNtfsVol_QueryClusterState(PRTFSNTFSVOL pThis, uint64_t iCluster, bool *pfState)
4570{
4571 uint64_t iClusterInCache = iCluster - pThis->iFirstBitmapCluster;
4572 if (iClusterInCache < pThis->cBitmapClusters)
4573 {
4574 *pfState = rtFsNtfsBitmap_IsSet(pThis->pvBitmap, (uint32_t)iClusterInCache);
4575 return VINF_SUCCESS;
4576 }
4577 return rtFsNtfsVol_QueryClusterStateSlow(pThis, iCluster, pfState);
4578}
4579
4580
4581/**
4582 * Callback for RTAvlU64Destroy used by rtFsNtfsVol_Close to destroy the MFT
4583 * record cache.
4584 *
4585 * @returns VINF_SUCCESS
4586 * @param pNode The MFT record to destroy.
4587 * @param pvUser Ignored.
4588 */
4589static DECLCALLBACK(int) rtFsNtFsVol_DestroyCachedMftRecord(PAVLU64NODECORE pNode, void *pvUser)
4590{
4591 PRTFSNTFSMFTREC pMftRec = (PRTFSNTFSMFTREC)pNode;
4592 RT_NOREF(pvUser);
4593
4594 RTMemFree(pMftRec->pbRec);
4595 pMftRec->pbRec = NULL;
4596 RTMemFree(pMftRec);
4597
4598 return VINF_SUCCESS;
4599}
4600
4601
4602
4603/**
4604 * Callback for RTAvlU64Destroy used by rtFsNtfsVol_Close to destroy the index
4605 * node cache.
4606 *
4607 * @returns VINF_SUCCESS
4608 * @param pNode The index node to destroy.
4609 * @param pvUser Ignored.
4610 */
4611static DECLCALLBACK(int) rtFsNtfsVol_DestroyIndexNode(PAVLU64NODECORE pNode, void *pvUser)
4612{
4613 PRTFSNTFSIDXNODE pIdxNode = (PRTFSNTFSIDXNODE)pNode;
4614 RT_NOREF(pvUser);
4615
4616 RTMemFree(pIdxNode->pNode);
4617 RTMemFree(pIdxNode->NodeInfo.papEntries);
4618 pIdxNode->pNode = NULL;
4619 pIdxNode->NodeInfo.papEntries = NULL;
4620 pIdxNode->NodeInfo.pIndexHdr = NULL;
4621 pIdxNode->NodeInfo.pVol = NULL;
4622 return VINF_SUCCESS;
4623}
4624
4625
4626/**
4627 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
4628 */
4629static DECLCALLBACK(int) rtFsNtfsVol_Close(void *pvThis)
4630{
4631 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4632 Log(("rtFsNtfsVol_Close(%p):\n", pThis));
4633
4634 /*
4635 * Index / directory related members.
4636 */
4637 if (pThis->pRootDir)
4638 {
4639 rtFsNtfsDirShrd_Release(pThis->pRootDir);
4640 pThis->pRootDir = NULL;
4641 }
4642
4643 RTAvlU64Destroy(&pThis->IdxNodeCacheRoot, rtFsNtfsVol_DestroyIndexNode, NULL);
4644
4645 RTMemFree(pThis->pawcUpcase);
4646 pThis->pawcUpcase = NULL;
4647
4648 pThis->IdxNodeUnusedHead.pPrev = pThis->IdxNodeUnusedHead.pNext = NULL;
4649
4650 /*
4651 * Allocation bitmap cache.
4652 */
4653 if (pThis->pMftBitmap)
4654 {
4655 rtFsNtfsCore_Release(pThis->pMftBitmap->pCore);
4656 pThis->pMftBitmap = NULL;
4657 }
4658 RTMemFree(pThis->pvBitmap);
4659 pThis->pvBitmap = NULL;
4660
4661 /*
4662 * The MFT and MFT cache.
4663 */
4664 if (pThis->pMftData)
4665 {
4666 rtFsNtfsCore_Release(pThis->pMftData->pCore);
4667 pThis->pMftData = NULL;
4668 }
4669
4670 Assert(RTListIsEmpty(&pThis->CoreInUseHead));
4671 PRTFSNTFSCORE pCurCore, pNextCore;
4672 RTListForEachSafe(&pThis->CoreInUseHead, pCurCore, pNextCore, RTFSNTFSCORE, ListEntry)
4673 rtFsNtfsCore_Destroy(pCurCore);
4674 RTListForEachSafe(&pThis->CoreUnusedHead, pCurCore, pNextCore, RTFSNTFSCORE, ListEntry)
4675 rtFsNtfsCore_Destroy(pCurCore);
4676
4677 pThis->CoreInUseHead.pPrev = pThis->CoreInUseHead.pNext = NULL;
4678 pThis->CoreUnusedHead.pPrev = pThis->CoreUnusedHead.pNext = NULL;
4679
4680 Assert(pThis->MftRoot == NULL);
4681 RTAvlU64Destroy(&pThis->MftRoot, rtFsNtFsVol_DestroyCachedMftRecord, NULL);
4682
4683 /*
4684 * Backing file and handles.
4685 */
4686 RTVfsFileRelease(pThis->hVfsBacking);
4687 pThis->hVfsBacking = NIL_RTVFSFILE;
4688 pThis->hVfsSelf = NIL_RTVFS;
4689
4690 return VINF_SUCCESS;
4691}
4692
4693
4694/**
4695 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
4696 */
4697static DECLCALLBACK(int) rtFsNtfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
4698{
4699 NOREF(pvThis); NOREF(pObjInfo); NOREF(enmAddAttr);
4700 return VERR_WRONG_TYPE;
4701}
4702
4703
4704/**
4705 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
4706 */
4707static DECLCALLBACK(int) rtFsNtfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
4708{
4709 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4710 AssertReturn(pThis->pRootDir, VERR_INTERNAL_ERROR_4);
4711 int rc = rtFsNtfsVol_NewDirFromShared(pThis, pThis->pRootDir, phVfsDir);
4712 LogFlow(("rtFsNtfsVol_OpenRoot: returns %Rrc\n", rc));
4713 return rc;
4714}
4715
4716
4717/**
4718 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
4719 */
4720static DECLCALLBACK(int) rtFsNtfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
4721{
4722 PRTFSNTFSVOL pThis = (PRTFSNTFSVOL)pvThis;
4723 *pfUsed = true;
4724
4725 /*
4726 * Round to a cluster range.
4727 */
4728 uint64_t iCluster = off >> pThis->cClusterShift;
4729
4730 Assert(RT_IS_POWER_OF_TWO(pThis->cbCluster));
4731 cb += off & (pThis->cbCluster - 1);
4732 cb = RT_ALIGN_Z(cb, pThis->cbCluster);
4733 size_t cClusters = cb >> pThis->cClusterShift;
4734
4735 /*
4736 * Check the clusters one-by-one.
4737 * Just to be cautious, we will always check the cluster at off, even when cb is zero.
4738 */
4739 do
4740 {
4741 bool fState = true;
4742 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
4743 if (RT_FAILURE(rc))
4744 return rc;
4745 if (fState)
4746 {
4747 *pfUsed = true;
4748 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - used\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
4749 return VINF_SUCCESS;
4750 }
4751
4752 iCluster++;
4753 } while (cClusters-- > 0);
4754
4755 LogFlow(("rtFsNtfsVol_QueryRangeState: %RX64 LB %#x - unused\n", off & ~(uint64_t)(pThis->cbCluster - 1), cb));
4756 *pfUsed = false;
4757 return VINF_SUCCESS;
4758}
4759
4760
4761/**
4762 * NTFS volume operations.
4763 */
4764static const RTVFSOPS g_rtFsNtfsVolOps =
4765{
4766 /* .Obj = */
4767 {
4768 /* .uVersion = */ RTVFSOBJOPS_VERSION,
4769 /* .enmType = */ RTVFSOBJTYPE_VFS,
4770 /* .pszName = */ "NtfsVol",
4771 /* .pfnClose = */ rtFsNtfsVol_Close,
4772 /* .pfnQueryInfo = */ rtFsNtfsVol_QueryInfo,
4773 /* .pfnQueryInfoEx = */ NULL,
4774 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
4775 },
4776 /* .uVersion = */ RTVFSOPS_VERSION,
4777 /* .fFeatures = */ 0,
4778 /* .pfnOpenRoot = */ rtFsNtfsVol_OpenRoot,
4779 /* .pfnQueryRangeState = */ rtFsNtfsVol_QueryRangeState,
4780 /* .uEndMarker = */ RTVFSOPS_VERSION
4781};
4782
4783
4784/**
4785 * Checks that the storage for the given attribute is all marked allocated in
4786 * the allocation bitmap of the volume.
4787 *
4788 * @returns IPRT status code.
4789 * @param pThis The NTFS volume instance.
4790 * @param pAttr The attribute to check.
4791 * @param pszDesc Description of the attribute.
4792 * @param pErrInfo Where to return error details.
4793 */
4794static int rtFsNtfsVolCheckBitmap(PRTFSNTFSVOL pThis, PRTFSNTFSATTR pAttr, const char *pszDesc, PRTERRINFO pErrInfo)
4795{
4796 PRTFSNTFSATTRSUBREC pSubRec = NULL;
4797 PRTFSNTFSEXTENTS pTable = &pAttr->Extents;
4798 uint64_t offFile = 0;
4799 for (;;)
4800 {
4801 uint32_t const cExtents = pTable->cExtents;
4802 PRTFSNTFSEXTENT paExtents = pTable->paExtents;
4803 for (uint32_t iExtent = 0; iExtent < cExtents; iExtent++)
4804 {
4805 uint64_t const off = paExtents[iExtent].off;
4806 if (off == UINT64_MAX)
4807 offFile += paExtents[iExtent].cbExtent;
4808 else
4809 {
4810 uint64_t iCluster = off >> pThis->cClusterShift;
4811 uint64_t cClusters = paExtents[iExtent].cbExtent >> pThis->cClusterShift;
4812 Assert((cClusters << pThis->cClusterShift) == paExtents[iExtent].cbExtent);
4813 Assert(cClusters != 0);
4814
4815 while (cClusters-- > 0)
4816 {
4817 bool fState = false;
4818 int rc = rtFsNtfsVol_QueryClusterState(pThis, iCluster, &fState);
4819 if (RT_FAILURE(rc))
4820 return RTERRINFO_LOG_REL_SET_F(pErrInfo, rc,
4821 "Error querying allocation bitmap entry %#RX64 (for %s offset %#RX64)",
4822 iCluster, pszDesc, offFile);
4823 if (!fState)
4824 return RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4825 "Cluster %#RX64 at offset %#RX64 in %s is not marked allocated",
4826 iCluster, offFile, pszDesc);
4827 offFile += pThis->cbCluster;
4828 }
4829 }
4830 }
4831
4832 /* Next table. */
4833 pSubRec = pSubRec ? pSubRec->pNext : pAttr->pSubRecHead;
4834 if (!pSubRec)
4835 return VINF_SUCCESS;
4836 pTable = &pSubRec->Extents;
4837 }
4838}
4839
4840
4841/**
4842 * Loads, validates and setups the '.' (NTFS_MFT_IDX_ROOT) MFT entry.
4843 *
4844 * @returns IPRT status code
4845 * @param pThis The NTFS volume instance. Will set pawcUpcase.
4846 * @param pErrInfo Where to return additional error info.
4847 */
4848static int rtFsNtfsVolLoadRootDir(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
4849{
4850 /*
4851 * Load it and do some checks.
4852 */
4853 PRTFSNTFSCORE pCore;
4854 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_ROOT, false /*fRelaxedUsa*/, &pCore, pErrInfo); // DON'T COMMIT
4855 if (RT_SUCCESS(rc))
4856 {
4857 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
4858 if (!pFilenameAttr)
4859 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: has no FILENAME attribute!");
4860 else if (pFilenameAttr->pAttrHdr->fNonResident)
4861 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: FILENAME attribute is non-resident!");
4862 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[1]))
4863 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4864 "RootDir: FILENAME attribute value size is too small: %#x",
4865 pFilenameAttr->pAttrHdr->u.Res.cbValue);
4866 else
4867 {
4868 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
4869 + pFilenameAttr->pAttrHdr->u.Res.offValue);
4870 if ( pFilename->cwcFilename != 1
4871 || ( RTUtf16NICmpAscii(pFilename->wszFilename, ".", 1) != 0
4872 && RTUtf16NICmpAscii(pFilename->wszFilename, "$", 1) != 0))
4873 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4874 "RootDir: FILENAME is not '.' nor '$: '%.*ls'",
4875 pFilename->cwcFilename, pFilename->wszFilename);
4876 else
4877 {
4878 PRTFSNTFSATTR pIndexRoot = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ROOT,
4879 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4880 PRTFSNTFSATTR pIndexAlloc = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_INDEX_ALLOCATION,
4881 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4882 PRTFSNTFSATTR pIndexBitmap = rtFsNtfsCore_FindNamedAttributeAscii(pCore, NTFS_AT_BITMAP,
4883 RT_STR_TUPLE(NTFS_DIR_ATTRIBUTE_NAME));
4884 if (!pIndexRoot)
4885 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ROOT attribute named $I30");
4886 else if (!pIndexAlloc && pIndexBitmap)
4887 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no INDEX_ALLOCATION attribute named $I30");
4888 else if (!pIndexBitmap && pIndexAlloc)
4889 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "RootDir: Found no BITMAP attribute named $I30");
4890 if (RT_SUCCESS(rc) && pIndexAlloc)
4891 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexAlloc, "RootDir", pErrInfo);
4892 if (RT_SUCCESS(rc) && pIndexBitmap)
4893 rc = rtFsNtfsVolCheckBitmap(pThis, pIndexBitmap, "RootDir/bitmap", pErrInfo);
4894 if (RT_SUCCESS(rc))
4895 {
4896 /*
4897 * Load it as a normal directory.
4898 */
4899 PRTFSNTFSDIRSHRD pSharedDir;
4900 rc = rtFsNtfsVol_NewSharedDirFromCore(pThis, pCore, &pSharedDir, pErrInfo, "RootDir");
4901 if (RT_SUCCESS(rc))
4902 {
4903 rtFsNtfsCore_Release(pCore);
4904 pThis->pRootDir = pSharedDir;
4905 return VINF_SUCCESS;
4906 }
4907 }
4908 }
4909 }
4910 rtFsNtfsCore_Release(pCore);
4911 }
4912 else
4913 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Root dir: Error reading MFT record");
4914 return rc;
4915}
4916
4917
4918/**
4919 * Loads, validates and setups the '$UpCase' (NTFS_MFT_IDX_UP_CASE) MFT entry.
4920 *
4921 * This is needed for filename lookups, I think.
4922 *
4923 * @returns IPRT status code
4924 * @param pThis The NTFS volume instance. Will set pawcUpcase.
4925 * @param pErrInfo Where to return additional error info.
4926 */
4927static int rtFsNtfsVolLoadUpCase(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
4928{
4929 PRTFSNTFSCORE pCore;
4930 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_UP_CASE, false /*fRelaxedUsa*/, &pCore, pErrInfo);
4931 if (RT_SUCCESS(rc))
4932 {
4933 PRTFSNTFSATTR pDataAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
4934 if (pDataAttr)
4935 {
4936 /*
4937 * Validate the '$Upcase' MFT record.
4938 */
4939 uint32_t const cbMin = 512;
4940 uint32_t const cbMax = _128K;
4941 if (!pDataAttr->pAttrHdr->fNonResident)
4942 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: unnamed DATA attribute is resident!");
4943 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) < cbMin
4944 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) > cbMax)
4945 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4946 "$UpCase: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX32",
4947 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated), cbMin, cbMax);
4948 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) < cbMin
4949 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
4950 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData)
4951 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData) & 1) )
4952 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4953 "$UpCase: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX32 and no more than %#RX64",
4954 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbData), cbMin,
4955 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
4956 else if ( (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) < cbMin
4957 || (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized)
4958 > (uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated)
4959 || ((uint64_t)RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbInitialized) & 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.cbInitialized), cbMin,
4963 RT_LE2H_U64(pDataAttr->pAttrHdr->u.NonRes.cbAllocated) );
4964 else if (pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit != 0)
4965 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4966 "$UpCase: unnamed DATA attribute is compressed: %#x",
4967 pDataAttr->pAttrHdr->u.NonRes.uCompressionUnit);
4968 else
4969 {
4970 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
4971 if (!pFilenameAttr)
4972 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase has no FILENAME attribute!");
4973 else if (pFilenameAttr->pAttrHdr->fNonResident)
4974 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase FILENAME attribute is non-resident!");
4975 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
4976 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4977 "$UpCase: FILENAME attribute value size is too small: %#x",
4978 pFilenameAttr->pAttrHdr->u.Res.cbValue);
4979 else
4980 {
4981 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
4982 + pFilenameAttr->pAttrHdr->u.Res.offValue);
4983 if ( pFilename->cwcFilename != 7
4984 || RTUtf16NICmpAscii(pFilename->wszFilename, "$UpCase", 7) != 0)
4985 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
4986 "$UpCase: FILENAME isn't '$UpCase': '%.*ls'",
4987 pFilename->cwcFilename, pFilename->wszFilename);
4988 else
4989 {
4990 /*
4991 * Allocate memory for the uppercase table and read it.
4992 */
4993 PRTUTF16 pawcUpcase;
4994 pThis->pawcUpcase = pawcUpcase = (PRTUTF16)RTMemAlloc(_64K * sizeof(pThis->pawcUpcase[0]));
4995 if (pawcUpcase)
4996 {
4997 for (size_t i = 0; i < _64K; i++)
4998 pawcUpcase[i] = (uint16_t)i;
4999
5000 rc = rtFsNtfsAttr_Read(pDataAttr, 0, pawcUpcase, pDataAttr->pAttrHdr->u.NonRes.cbData);
5001 if (RT_SUCCESS(rc))
5002 {
5003 /*
5004 * Check the data.
5005 */
5006 for (size_t i = 1; i < _64K; i++)
5007 if (pawcUpcase[i] == 0)
5008 {
5009 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5010 "$UpCase entry %#x is zero!", i);
5011 break;
5012 }
5013
5014 /*
5015 * While we still have the $UpCase file open, check it against the allocation bitmap.
5016 */
5017 if (RT_SUCCESS(rc))
5018 rc = rtFsNtfsVolCheckBitmap(pThis, pDataAttr, "$UpCase", pErrInfo);
5019
5020 /* We're done, no need for special success return here though. */
5021 }
5022 else
5023 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, rc, "Error reading $UpCase data into memory");
5024 }
5025 else
5026 rc = VERR_NO_MEMORY;
5027 }
5028 }
5029 }
5030 }
5031 else
5032 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$UpCase: has no unnamed DATA attribute!");
5033 rtFsNtfsCore_Release(pCore);
5034 }
5035 else
5036 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$UpCase: Error reading the MFT record");
5037 return rc;
5038}
5039
5040
5041/**
5042 * Loads the allocation bitmap and does basic validation of.
5043 *
5044 * @returns IPRT status code.
5045 * @param pThis The NTFS volume instance. Will set up the
5046 * 'Allocation bitmap and cache' fields.
5047 * @param pErrInfo Where to return error details.
5048 */
5049static int rtFsNtfsVolLoadBitmap(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5050{
5051 PRTFSNTFSCORE pCore;
5052 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_BITMAP, false /*fRelaxedUsa*/, &pCore, pErrInfo);
5053 if (RT_SUCCESS(rc))
5054 {
5055 PRTFSNTFSATTR pMftBitmap;
5056 pThis->pMftBitmap = pMftBitmap = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
5057 if (pMftBitmap)
5058 {
5059 /*
5060 * Validate the '$Bitmap' MFT record.
5061 * We expect the bitmap to be fully initialized and be sized according to the
5062 * formatted volume size. Allegedly, NTFS pads it to an even 8 byte in size.
5063 */
5064 uint64_t const cbMinBitmap = RT_ALIGN_64(pThis->cbVolume >> (pThis->cClusterShift + 3), 8);
5065 uint64_t const cbMaxBitmap = RT_ALIGN_64(cbMinBitmap, pThis->cbCluster);
5066 //uint64_t const cbMinInitialized = RT_ALIGN_64((RT_MAX(pThis->uLcnMft, pThis->uLcnMftMirror) + 16) >> 3, 8);
5067 if (!pMftBitmap->pAttrHdr->fNonResident)
5068 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is resident!");
5069 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) < cbMinBitmap
5070 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) > cbMaxBitmap)
5071 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5072 "$Bitmap: unnamed DATA attribute allocated size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5073 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated), cbMinBitmap, cbMaxBitmap);
5074 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData) < cbMinBitmap
5075 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData)
5076 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData))
5077 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5078 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5079 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbData), cbMinBitmap,
5080 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
5081 else if ( (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized) < cbMinBitmap
5082 || (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized)
5083 > (uint64_t)RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated))
5084 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5085 "$Bitmap: unnamed DATA attribute initialized size is out of range: %#RX64, expected at least %#RX64 and no more than %#RX64",
5086 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbInitialized), cbMinBitmap,
5087 RT_LE2H_U64(pMftBitmap->pAttrHdr->u.NonRes.cbAllocated) );
5088 else if (pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit != 0)
5089 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5090 "$Bitmap: unnamed DATA attribute is compressed: %#x",
5091 pMftBitmap->pAttrHdr->u.NonRes.uCompressionUnit);
5092 else if (pMftBitmap->Extents.cExtents != 1) /* paranoia for now */
5093 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5094 "$Bitmap: unnamed DATA attribute is expected to have a single extent: %u extents",
5095 pMftBitmap->Extents.cExtents);
5096 else if (pMftBitmap->Extents.paExtents[0].off == UINT64_MAX)
5097 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 unnamed DATA attribute is sparse");
5098 else
5099 {
5100 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5101 if (!pFilenameAttr)
5102 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 has no FILENAME attribute!");
5103 else if (pFilenameAttr->pAttrHdr->fNonResident)
5104 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #6 FILENAME attribute is non-resident!");
5105 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
5106 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5107 "$Bitmap FILENAME attribute value size is too small: %#x",
5108 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5109 else
5110 {
5111 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5112 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5113 if ( pFilename->cwcFilename != 7
5114 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Bitmap", 7) != 0)
5115 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5116 "$Bitmap: FILENAME isn't '$Bitmap': '%.*ls'",
5117 pFilename->cwcFilename, pFilename->wszFilename);
5118 else
5119 {
5120 /*
5121 * Read some of it into the buffer and check that essential stuff is flagged as allocated.
5122 */
5123 /* The boot sector. */
5124 bool fState = false;
5125 rc = rtFsNtfsVol_QueryClusterState(pThis, 0, &fState);
5126 if (RT_SUCCESS(rc) && !fState)
5127 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5128 "MFT allocation bitmap error: Bootsector isn't marked allocated!");
5129 else if (RT_FAILURE(rc))
5130 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5131 "MFT allocation bitmap (offset 0) read error: %Rrc", rc);
5132
5133 /* The bitmap ifself, the MFT data, and the MFT bitmap. */
5134 if (RT_SUCCESS(rc))
5135 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftBitmap, "allocation bitmap", pErrInfo);
5136 if (RT_SUCCESS(rc))
5137 rc = rtFsNtfsVolCheckBitmap(pThis, pThis->pMftData, "MFT", pErrInfo);
5138 if (RT_SUCCESS(rc))
5139 rc = rtFsNtfsVolCheckBitmap(pThis,
5140 rtFsNtfsCore_FindUnnamedAttribute(pThis->pMftData->pCore, NTFS_AT_BITMAP),
5141 "MFT Bitmap", pErrInfo);
5142 if (RT_SUCCESS(rc))
5143 {
5144 /*
5145 * Looks like the bitmap is good.
5146 */
5147 return VINF_SUCCESS;
5148 }
5149 }
5150 }
5151 }
5152 pThis->pMftBitmap = NULL;
5153 }
5154 else
5155 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Bitmap: has no unnamed DATA attribute!");
5156 rtFsNtfsCore_Release(pCore);
5157 }
5158 else
5159 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "$Bitmap: Error MFT record");
5160 return rc;
5161}
5162
5163
5164/**
5165 * Loads, validates and setups the '$Volume' (NTFS_MFT_IDX_VOLUME) MFT entry.
5166 *
5167 * @returns IPRT status code
5168 * @param pThis The NTFS volume instance. Will set uNtfsVersion
5169 * and fVolumeFlags.
5170 * @param pErrInfo Where to return additional error info.
5171 */
5172static int rtFsNtfsVolLoadVolumeInfo(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5173{
5174 PRTFSNTFSCORE pCore;
5175 int rc = rtFsNtfsVol_NewCoreForMftIdx(pThis, NTFS_MFT_IDX_VOLUME, false /*fRelaxedUsa*/, &pCore, pErrInfo);
5176 if (RT_SUCCESS(rc))
5177 {
5178 PRTFSNTFSATTR pVolInfoAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_VOLUME_INFORMATION);
5179 if (pVolInfoAttr)
5180 {
5181 /*
5182 * Validate the '$Volume' MFT record.
5183 */
5184 if (pVolInfoAttr->pAttrHdr->fNonResident)
5185 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume unnamed VOLUME_INFORMATION attribute is not resident!");
5186 else if ( pVolInfoAttr->cbResident != sizeof(NTFSATVOLUMEINFO)
5187 || pVolInfoAttr->cbValue != sizeof(NTFSATVOLUMEINFO))
5188 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5189 "$Volume VOLUME_INFORMATION attribute has the wrong size: cbValue=%#RX64, cbResident=%#RX32, expected %#x\n",
5190 pVolInfoAttr->cbValue, pVolInfoAttr->cbResident, sizeof(NTFSATVOLUMEINFO));
5191 else
5192 {
5193 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5194 if (!pFilenameAttr)
5195 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume has no FILENAME attribute!");
5196 else if (pFilenameAttr->pAttrHdr->fNonResident)
5197 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "$Volume FILENAME attribute is non-resident!");
5198 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[7]))
5199 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5200 "$Volume FILENAME attribute value size is too small: %#x",
5201 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5202 else
5203 {
5204 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5205 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5206 if ( pFilename->cwcFilename != 7
5207 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Volume", 7) != 0)
5208 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5209 "$Volume FILENAME isn't '$Volume': '%.*ls'",
5210 pFilename->cwcFilename, pFilename->wszFilename);
5211 else
5212 {
5213 /*
5214 * Look at the information.
5215 */
5216 PCNTFSATVOLUMEINFO pVolInfo;
5217 pVolInfo = (PCNTFSATVOLUMEINFO)((uint8_t *)pVolInfoAttr->pAttrHdr + pVolInfoAttr->pAttrHdr->u.Res.offValue);
5218 pThis->uNtfsVersion = RTFSNTFS_MAKE_VERSION(pVolInfo->uMajorVersion, pVolInfo->uMinorVersion);
5219 pThis->fVolumeFlags = RT_LE2H_U16(pVolInfo->fFlags);
5220 Log(("NTFS: Version %u.%u, flags=%#x\n", pVolInfo->uMajorVersion, pVolInfo->uMinorVersion, pThis->fVolumeFlags));
5221
5222 /* We're done, no need for special success return here though. */
5223 }
5224 }
5225 }
5226 }
5227 else
5228 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5229 "MFT record $Volume has no unnamed VOLUME_INFORMATION attribute!");
5230 rtFsNtfsCore_Release(pCore);
5231 }
5232 else
5233 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading $Volume MFT record");
5234 return rc;
5235}
5236
5237
5238/**
5239 * Loads, validates and setups the '$Mft' (NTFS_MFT_IDX_MFT) MFT entry.
5240 *
5241 * This is the first thing we do after we've checked out the boot sector and
5242 * extracted information from it, since everything else depends on us being able
5243 * to access the MFT data.
5244 *
5245 * @returns IPRT status code
5246 * @param pThis The NTFS volume instance. Will set pMftData.
5247 * @param pErrInfo Where to return additional error info.
5248 */
5249static int rtFsNtfsVolLoadMft(PRTFSNTFSVOL pThis, PRTERRINFO pErrInfo)
5250{
5251 /*
5252 * Bootstrap the MFT data stream.
5253 */
5254 PRTFSNTFSMFTREC pRec = rtFsNtfsVol_NewMftRec(pThis, NTFS_MFT_IDX_MFT);
5255 AssertReturn(pRec, VERR_NO_MEMORY);
5256
5257#if 0 && defined(LOG_ENABLED)
5258 for (uint32_t i = 0; i < 128; i++)
5259 {
5260 uint64_t const offDisk = (pThis->uLcnMft << pThis->cClusterShift) + i * pThis->cbMftRecord;
5261 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
5262 if (RT_SUCCESS(rc))
5263 {
5264 pRec->TreeNode.Key = i;
5265 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
5266 pRec->TreeNode.Key = 0;
5267 }
5268 }
5269#endif
5270
5271 uint64_t const offDisk = pThis->uLcnMft << pThis->cClusterShift;
5272 int rc = RTVfsFileReadAt(pThis->hVfsBacking, offDisk, pRec->pbRec, pThis->cbMftRecord, NULL);
5273 if (RT_SUCCESS(rc))
5274 {
5275 rc = rtFsNtfsRec_DoMultiSectorFixups(&pRec->pFileRec->Hdr, pThis->cbMftRecord, true, pErrInfo);
5276 if (RT_SUCCESS(rc))
5277 {
5278#ifdef LOG_ENABLED
5279 rtfsNtfsMftRec_Log(pRec, pThis->cbMftRecord);
5280#endif
5281 rc = rtFsNtfsVol_ParseMft(pThis, pRec, pErrInfo);
5282 }
5283 if (RT_SUCCESS(rc))
5284 {
5285 PRTFSNTFSCORE pCore = pRec->pCore;
5286 PRTFSNTFSATTR pMftData;
5287 pThis->pMftData = pMftData = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_DATA);
5288 if (pMftData)
5289 {
5290 /*
5291 * Validate the '$Mft' MFT record.
5292 */
5293 PNTFSATTRIBHDR pAttrHdr = pMftData->pAttrHdr;
5294 if (!pAttrHdr->fNonResident)
5295 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 unnamed DATA attribute is resident!");
5296 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated) < pThis->cbMftRecord * 16U
5297 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated) >= pThis->cbBacking)
5298 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5299 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
5300 RT_LE2H_U64(pAttrHdr->u.NonRes.cbAllocated));
5301 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized) < pThis->cbMftRecord * 16U
5302 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized) >= pThis->cbBacking)
5303 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5304 "MFT record #0 unnamed DATA attribute initialized size is out of range: %#RX64",
5305 RT_LE2H_U64(pAttrHdr->u.NonRes.cbInitialized));
5306 else if ( (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbData) < pThis->cbMftRecord * 16U
5307 || (uint64_t)RT_LE2H_U64(pAttrHdr->u.NonRes.cbData) >= pThis->cbBacking)
5308 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5309 "MFT record #0 unnamed DATA attribute allocated size is out of range: %#RX64",
5310 RT_LE2H_U64(pAttrHdr->u.NonRes.cbData));
5311 else if (pAttrHdr->u.NonRes.uCompressionUnit != 0)
5312 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5313 "MFT record #0 unnamed DATA attribute is compressed: %#x",
5314 pAttrHdr->u.NonRes.uCompressionUnit);
5315 else if (pMftData->Extents.cExtents == 0)
5316 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5317 "MFT record #0 unnamed DATA attribute has no data on the disk");
5318 else if (pMftData->Extents.paExtents[0].off != offDisk)
5319 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5320 "MFT record #0 unnamed DATA attribute has a bogus disk offset: %#RX64, expected %#RX64",
5321 pMftData->Extents.paExtents[0].off, offDisk);
5322 else if (!rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_BITMAP))
5323 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed BITMAP attribute!");
5324 else
5325 {
5326 PRTFSNTFSATTR pFilenameAttr = rtFsNtfsCore_FindUnnamedAttribute(pCore, NTFS_AT_FILENAME);
5327 if (!pFilenameAttr)
5328 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no FILENAME attribute!");
5329 else if (pFilenameAttr->pAttrHdr->fNonResident)
5330 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 FILENAME attribute is non-resident!");
5331 else if (pFilenameAttr->pAttrHdr->u.Res.cbValue < RT_UOFFSETOF(NTFSATFILENAME, wszFilename[4]))
5332 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5333 "MFT record #0 FILENAME attribute value size is too small: %#x",
5334 pFilenameAttr->pAttrHdr->u.Res.cbValue);
5335 else
5336 {
5337 PNTFSATFILENAME pFilename = (PNTFSATFILENAME)( (uint8_t *)pFilenameAttr->pAttrHdr
5338 + pFilenameAttr->pAttrHdr->u.Res.offValue);
5339 if ( pFilename->cwcFilename != 4
5340 || RTUtf16NICmpAscii(pFilename->wszFilename, "$Mft", 4) != 0)
5341 rc = RTERRINFO_LOG_REL_SET_F(pErrInfo, VERR_VFS_BOGUS_FORMAT,
5342 "MFT record #0 FILENAME isn't '$Mft': '%.*ls'",
5343 pFilename->cwcFilename, pFilename->wszFilename);
5344 else
5345 {
5346 /*
5347 * Looks like we're good. Insert core record into the cache.
5348 */
5349 RTListAppend(&pThis->CoreInUseHead, &pCore->ListEntry);
5350 pThis->cbCoreObjects += pCore->cbCost;
5351
5352 Assert(pCore->cRefs == 1);
5353 Assert(pRec->cRefs == 2);
5354 rtFsNtfsMftRec_Release(pRec, pThis);
5355
5356 return VINF_SUCCESS;
5357 }
5358 }
5359 }
5360 pThis->pMftData = NULL;
5361 }
5362 else
5363 rc = RTERRINFO_LOG_REL_SET(pErrInfo, VERR_VFS_BOGUS_FORMAT, "MFT record #0 has no unnamed DATA attribute!");
5364 }
5365 if (pRec->pCore)
5366 rtFsNtfsCore_Destroy(pRec->pCore);
5367 rtFsNtfsMftRec_Release(pRec, pThis);
5368 }
5369 else
5370 rc = RTERRINFO_LOG_REL_SET(pErrInfo, rc, "Error reading MFT record #0");
5371 return rc;
5372}
5373
5374
5375/**
5376 * Loads the bootsector and parses it, copying values into the instance data.
5377 *
5378 * @returns IRPT status code.
5379 * @param pThis The instance data.
5380 * @param pvBuf The buffer.
5381 * @param cbBuf The buffer size.
5382 * @param pErrInfo Where to return additional error details.
5383 */
5384static int rtFsNtfsVolLoadAndParseBootsector(PRTFSNTFSVOL pThis, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
5385{
5386 AssertReturn(cbBuf >= sizeof(FATBOOTSECTOR), VERR_INTERNAL_ERROR_2);
5387
5388 /*
5389 * Read the boot sector and check that it makes sense for a NTFS volume.
5390 *
5391 * Note! There are two potential backup locations of the boot sector, however we
5392 * currently don't implement falling back on these on corruption/read errors.
5393 */
5394 PFATBOOTSECTOR pBootSector = (PFATBOOTSECTOR)pvBuf;
5395 int rc = RTVfsFileReadAt(pThis->hVfsBacking, 0, pBootSector, sizeof(*pBootSector), NULL);
5396 if (RT_FAILURE(rc))
5397 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading boot sector");
5398
5399 if (memcmp(pBootSector->achOemName, RT_STR_TUPLE(NTFS_OEM_ID_MAGIC)) != 0)
5400 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5401 "Not NTFS - OEM field mismatch: %.8Rhxs'", pBootSector->achOemName);
5402
5403 /* Check must-be-zero BPB fields. */
5404 if (pBootSector->Bpb.Ntfs.Bpb.cReservedSectors != 0)
5405 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cReservedSectors=%u",
5406 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cReservedSectors));
5407 if (pBootSector->Bpb.Ntfs.Bpb.cFats != 0)
5408 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cFats=%u",
5409 pBootSector->Bpb.Ntfs.Bpb.cFats);
5410 if (pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries != 0)
5411 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cMaxRootDirEntries=%u",
5412 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cMaxRootDirEntries));
5413 if (pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16 != 0)
5414 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cTotalSectors16=%u",
5415 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cTotalSectors16));
5416 if (pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat != 0)
5417 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - MBZ: BPB.cSectorsPerFat=%u",
5418 RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerFat));
5419
5420 /* Check other relevant BPB fields. */
5421 uint32_t cbSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cbSector);
5422 if ( cbSector != 512
5423 && cbSector != 1024
5424 && cbSector != 2048
5425 && cbSector != 4096)
5426 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not NTFS - BPB.cbSector is ouf of range: %u", cbSector);
5427 pThis->cbSector = cbSector;
5428 Log2(("NTFS BPB: cbSector=%#x\n", cbSector));
5429
5430 uint32_t cClusterPerSector = RT_LE2H_U16(pBootSector->Bpb.Ntfs.Bpb.cSectorsPerCluster);
5431 if ( !RT_IS_POWER_OF_TWO(cClusterPerSector)
5432 || cClusterPerSector == 0)
5433 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
5434 "Not NTFS - BPB.cCluster is ouf of range: %u", cClusterPerSector);
5435
5436 pThis->cbCluster = cClusterPerSector * cbSector;
5437 if (pThis->cbCluster > _64K)
5438 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5439 "cluster size exceeds 64KB: %#x", pThis->cbCluster);
5440 pThis->cClusterShift = ASMBitFirstSetU32(pThis->cbCluster) - 1;
5441 Log2(("NTFS BPB: cClusterPerSector=%#x => %#x bytes, %u shift\n", cClusterPerSector, pThis->cbCluster, pThis->cClusterShift));
5442 pThis->iMaxVirtualCluster = (uint64_t)INT64_MAX >> pThis->cClusterShift;
5443 Log2(("NTFS BPB: iMaxVirtualCluster=%#RX64\n", pThis->iMaxVirtualCluster));
5444
5445 /* NTFS BPB: cSectors. */
5446 uint64_t cSectors = RT_LE2H_U64(pBootSector->Bpb.Ntfs.cSectors);
5447 if (cSectors > pThis->cbBacking / pThis->cbSector)
5448 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5449 "NTFS sector count exceeds volume size: %#RX64 vs %#RX64",
5450 cSectors, pThis->cbBacking / pThis->cbSector);
5451 if (cSectors < 256)
5452 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "NTFS sector count too small: %#RX64", cSectors);
5453 pThis->cbVolume = cSectors * pThis->cbSector;
5454 pThis->cClusters = cSectors / cClusterPerSector;
5455 Log2(("NTFS BPB: cSectors=%#RX64 => %#xu bytes (%Rhcb) => cClusters=%#RX64\n",
5456 cSectors, pThis->cbVolume, pThis->cbVolume, pThis->cClusters));
5457
5458 /* NTFS BPB: MFT location. */
5459 uint64_t uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMft);
5460 if ( uLcn < 1
5461 || uLcn >= pThis->cClusters)
5462 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5463 "NTFS MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
5464 pThis->uLcnMft = uLcn;
5465 Log2(("NTFS BPB: uLcnMft=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
5466
5467 /* NTFS BPB: Mirror MFT location. */
5468 uLcn = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uLcnMftMirror);
5469 if ( uLcn < 1
5470 || uLcn >= pThis->cClusters)
5471 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5472 "NTFS mirror MFT location is out of bounds: %#RX64 (%#RX64 clusters)", uLcn, pThis->cClusters);
5473 pThis->uLcnMftMirror = uLcn;
5474 Log2(("NTFS BPB: uLcnMftMirror=%#RX64 (byte offset %#RX64)\n", uLcn, uLcn << pThis->cClusterShift));
5475
5476 /* NTFS BPB: Size of MFT file record. */
5477 if (pBootSector->Bpb.Ntfs.cClustersPerMftRecord >= 0)
5478 {
5479 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord)
5480 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord == 0)
5481 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5482 "NTFS clusters-per-mft-record value is zero or not a power of two: %#x",
5483 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
5484 pThis->cbMftRecord = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerMftRecord << pThis->cClusterShift;
5485 Assert(pThis->cbMftRecord == pBootSector->Bpb.Ntfs.cClustersPerMftRecord * pThis->cbCluster);
5486 }
5487 else if ( pBootSector->Bpb.Ntfs.cClustersPerMftRecord < -20
5488 || pBootSector->Bpb.Ntfs.cClustersPerMftRecord > -9)
5489 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5490 "NTFS clusters-per-mft-record is out of shift range: %d",
5491 pBootSector->Bpb.Ntfs.cClustersPerMftRecord);
5492 else
5493 pThis->cbMftRecord = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
5494 Log2(("NTFS BPB: cbMftRecord=%#x\n", pThis->cbMftRecord));
5495 if ( pThis->cbMftRecord > _32K
5496 || pThis->cbMftRecord < 256)
5497 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5498 "Unsupported NTFS MFT record size: %#x", pThis->cbMftRecord);
5499
5500 /* NTFS BPB: Default index node size */
5501 if (pBootSector->Bpb.Ntfs.cClustersPerIndexNode >= 0)
5502 {
5503 if ( !RT_IS_POWER_OF_TWO((uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode)
5504 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode == 0)
5505 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5506 "NTFS default clusters-per-index-tree-node is zero or not a power of two: %#x",
5507 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
5508 pThis->cbDefaultIndexNode = (uint32_t)pBootSector->Bpb.Ntfs.cClustersPerIndexNode << pThis->cClusterShift;
5509 Assert(pThis->cbDefaultIndexNode == pBootSector->Bpb.Ntfs.cClustersPerIndexNode * pThis->cbCluster);
5510 }
5511 else if ( pBootSector->Bpb.Ntfs.cClustersPerIndexNode < -32
5512 || pBootSector->Bpb.Ntfs.cClustersPerIndexNode > -9)
5513 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
5514 "NTFS default clusters-per-index-tree-node is out of shift range: %d",
5515 pBootSector->Bpb.Ntfs.cClustersPerIndexNode);
5516 else
5517 pThis->cbDefaultIndexNode = UINT32_C(1) << -pBootSector->Bpb.Ntfs.cClustersPerMftRecord;
5518 Log2(("NTFS BPB: cbDefaultIndexNode=%#x\n", pThis->cbDefaultIndexNode));
5519
5520 pThis->uSerialNo = RT_LE2H_U64(pBootSector->Bpb.Ntfs.uSerialNumber);
5521 Log2(("NTFS BPB: uSerialNo=%#x\n", pThis->uSerialNo));
5522
5523
5524 return VINF_SUCCESS;
5525}
5526
5527
5528RTDECL(int) RTFsNtfsVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fNtfsFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
5529{
5530 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
5531 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
5532 AssertReturn(!fNtfsFlags, VERR_INVALID_FLAGS);
5533
5534 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
5535 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
5536
5537 /*
5538 * Create a VFS instance and initialize the data so rtFsNtfsVol_Close works.
5539 */
5540 RTVFS hVfs;
5541 PRTFSNTFSVOL pThis;
5542 int rc = RTVfsNew(&g_rtFsNtfsVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
5543 if (RT_SUCCESS(rc))
5544 {
5545 pThis->hVfsBacking = hVfsFileIn;
5546 pThis->hVfsSelf = hVfs;
5547 pThis->fMntFlags = fMntFlags;
5548 pThis->fNtfsFlags = fNtfsFlags;
5549 RTListInit(&pThis->CoreInUseHead);
5550 RTListInit(&pThis->CoreUnusedHead);
5551 RTListInit(&pThis->IdxNodeUnusedHead);
5552
5553 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
5554 if (RT_SUCCESS(rc))
5555 {
5556 void *pvBuf = RTMemTmpAlloc(_64K);
5557 if (pvBuf)
5558 {
5559 rc = rtFsNtfsVolLoadAndParseBootsector(pThis, pvBuf, _64K, pErrInfo);
5560 if (RT_SUCCESS(rc))
5561 rc = rtFsNtfsVolLoadMft(pThis, pErrInfo);
5562 if (RT_SUCCESS(rc))
5563 rc = rtFsNtfsVolLoadVolumeInfo(pThis, pErrInfo);
5564 if (RT_SUCCESS(rc))
5565 rc = rtFsNtfsVolLoadBitmap(pThis, pErrInfo);
5566 if (RT_SUCCESS(rc))
5567 rc = rtFsNtfsVolLoadUpCase(pThis, pErrInfo);
5568 if (RT_SUCCESS(rc))
5569 rc = rtFsNtfsVolLoadRootDir(pThis, pErrInfo);
5570 RTMemTmpFree(pvBuf);
5571 if (RT_SUCCESS(rc))
5572 {
5573 *phVfs = hVfs;
5574 return VINF_SUCCESS;
5575 }
5576 }
5577 else
5578 rc = VERR_NO_TMP_MEMORY;
5579 }
5580
5581 RTVfsRelease(hVfs);
5582 *phVfs = NIL_RTVFS;
5583 }
5584 else
5585 RTVfsFileRelease(hVfsFileIn);
5586
5587 return rc;
5588}
5589
5590
5591/**
5592 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
5593 */
5594static DECLCALLBACK(int) rtVfsChainNtfsVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
5595 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
5596{
5597 RT_NOREF(pProviderReg);
5598
5599 /*
5600 * Basic checks.
5601 */
5602 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
5603 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
5604 if ( pElement->enmType != RTVFSOBJTYPE_VFS
5605 && pElement->enmType != RTVFSOBJTYPE_DIR)
5606 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
5607 if (pElement->cArgs > 1)
5608 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
5609
5610 /*
5611 * Parse the flag if present, save in pElement->uProvider.
5612 */
5613 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
5614 if (pElement->cArgs > 0)
5615 {
5616 const char *psz = pElement->paArgs[0].psz;
5617 if (*psz)
5618 {
5619 if (!strcmp(psz, "ro"))
5620 fReadOnly = true;
5621 else if (!strcmp(psz, "rw"))
5622 fReadOnly = false;
5623 else
5624 {
5625 *poffError = pElement->paArgs[0].offSpec;
5626 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
5627 }
5628 }
5629 }
5630
5631 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
5632 return VINF_SUCCESS;
5633}
5634
5635
5636/**
5637 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
5638 */
5639static DECLCALLBACK(int) rtVfsChainNtfsVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
5640 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
5641 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
5642{
5643 RT_NOREF(pProviderReg, pSpec, poffError);
5644
5645 int rc;
5646 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
5647 if (hVfsFileIn != NIL_RTVFSFILE)
5648 {
5649 RTVFS hVfs;
5650 rc = RTFsNtfsVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
5651 RTVfsFileRelease(hVfsFileIn);
5652 if (RT_SUCCESS(rc))
5653 {
5654 *phVfsObj = RTVfsObjFromVfs(hVfs);
5655 RTVfsRelease(hVfs);
5656 if (*phVfsObj != NIL_RTVFSOBJ)
5657 return VINF_SUCCESS;
5658 rc = VERR_VFS_CHAIN_CAST_FAILED;
5659 }
5660 }
5661 else
5662 rc = VERR_VFS_CHAIN_CAST_FAILED;
5663 return rc;
5664}
5665
5666
5667/**
5668 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
5669 */
5670static DECLCALLBACK(bool) rtVfsChainNtfsVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
5671 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
5672 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
5673{
5674 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
5675 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
5676 || !pReuseElement->paArgs[0].uProvider)
5677 return true;
5678 return false;
5679}
5680
5681
5682/** VFS chain element 'file'. */
5683static RTVFSCHAINELEMENTREG g_rtVfsChainNtfsVolReg =
5684{
5685 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
5686 /* fReserved = */ 0,
5687 /* pszName = */ "ntfs",
5688 /* ListEntry = */ { NULL, NULL },
5689 /* pszHelp = */ "Open a NTFS file system, requires a file object on the left side.\n"
5690 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
5691 /* pfnValidate = */ rtVfsChainNtfsVol_Validate,
5692 /* pfnInstantiate = */ rtVfsChainNtfsVol_Instantiate,
5693 /* pfnCanReuseElement = */ rtVfsChainNtfsVol_CanReuseElement,
5694 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
5695};
5696
5697RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainNtfsVolReg, rtVfsChainNtfsVolReg);
5698
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