VirtualBox

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

Last change on this file since 94291 was 94291, checked in by vboxsync, 3 years ago

IPRT,Storage: Adding RTVfsQueryLabel and internally a generic pfnQueryInfoEx method to the RTVFSOBJOPS function table. Untested implementation of the latter for iso/udf. bugref:9781

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

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