VirtualBox

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

Last change on this file since 72526 was 72273, checked in by vboxsync, 7 years ago

IPRT: Tweaked ntfsvfs.cpp and RTCp.cpp so it's patch files, provided the size doesn't change and they're large enough to not live in the MFT. This is very crude, but it's enormously helpful for tweaking the kernel.

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