VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use