VirtualBox

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

Last change on this file since 76734 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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